/*-------------------------------------------------------------------------
 *
 * parse_relation.c
 *	  解析器支持处理关系的例程
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/parser/parse_relation.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include <ctype.h>

#include "access/htup_details.h"
#include "access/relation.h"
#include "access/sysattr.h"
#include "access/table.h"
#include "catalog/heap.h"
#include "catalog/namespace.h"
#include "catalog/pg_type.h"
#include "funcapi.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "parser/parse_enr.h"
#include "parser/parse_relation.h"
#include "parser/parse_type.h"
#include "parser/parsetree.h"
#include "storage/lmgr.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/syscache.h"
#include "utils/varlena.h"


/*
 * 支持模糊匹配列。
 *
 * 这用于构建诊断消息，其中建议向用户非精确匹配的属性。该结构字段可以是特定 RTE 的方面，
 * 或整个范围表的方面，具体取决于上下文。
 */
typedef struct
{
	int			distance;		/* 加权距离（迄今为止最低） */
	RangeTblEntry *rfirst;		/* 第一个 RTE */
	AttrNumber	first;			/* 迄今为止最近的属性 */
	RangeTblEntry *rsecond;		/* 第二个 RTE */
	AttrNumber	second;			/* 迄今为止第二最近的属性 */
} FuzzyAttrMatchState;

#define MAX_FUZZY_DISTANCE				3


static ParseNamespaceItem *fc_scanNameSpaceForRefname(ParseState *fc_pstate,
												   const char *fc_refname,
												   int fc_location);
static ParseNamespaceItem *fc_scanNameSpaceForRelid(ParseState *fc_pstate, Oid fc_relid,
												 int fc_location);
static void fc_check_lateral_ref_ok(ParseState *fc_pstate, ParseNamespaceItem *fc_nsitem,
								 int fc_location);
static int	fc_scanRTEForColumn(ParseState *fc_pstate, RangeTblEntry *fc_rte,
							 Alias *fc_eref,
							 const char *fc_colname, int fc_location,
							 int fc_fuzzy_rte_penalty,
							 FuzzyAttrMatchState *fc_fuzzystate);
static void fc_markRTEForSelectPriv(ParseState *fc_pstate,
								 int fc_rtindex, AttrNumber fc_col);
static void fc_expandRelation(Oid fc_relid, Alias *fc_eref,
						   int fc_rtindex, int fc_sublevels_up,
						   int fc_location, bool fc_include_dropped,
						   List **fc_colnames, List **fc_colvars);
static void fc_expandTupleDesc(TupleDesc fc_tupdesc, Alias *fc_eref,
							int fc_count, int fc_offset,
							int fc_rtindex, int fc_sublevels_up,
							int fc_location, bool fc_include_dropped,
							List **fc_colnames, List **fc_colvars);
static int	fc_specialAttNum(const char *fc_attname);
static bool fc_isQueryUsingTempRelation_walker(Node *fc_node, void *fc_context);


/*
 * refnameNamespaceItem
 *	  给定一个可能带有限定的 refname，检查它是否与任何可见的
 *	  命名空间项匹配。如果匹配，则返回 nsitem 的指针；否则返回 NULL。
 *
 *	  可选地将 nsitem 的嵌套深度 (0 = 当前) 获取到 *sublevels_up。
 *	  如果 sublevels_up 为 NULL，则只考虑当前嵌套级别的项。
 *
 * 一个没有限定的 refname (schemaname == NULL) 可以匹配任何具有匹配
 * 别名的项，或者在没有别名的关系项的情况下匹配匹配的无别名关系名。
 * 可能这样的 refname 会匹配在最近的嵌套级别中有匹配的多个项；
 * 如果是这样，我们通过 ereport() 报告错误。
 *
 * 一个有限定的 refname (schemaname != NULL) 只能匹配一个关系项
 * (a) 没有别名并且 (b) 是与 schemaname.refname 相同关系的关系项。
 * 在这种情况下，我们将 schemaname.refname 转换为关系 OID 并通过
 * relid 搜索，而不是通过别名名称。这是特殊的，但这是 SQL 所要求的。
 */
ParseNamespaceItem * refnameNamespaceItem(ParseState *fc_pstate,
					 const char *fc_schemaname,
					 const char *fc_refname,
					 int fc_location,
					 int *fc_sublevels_up)
{
	Oid			fc_relId = InvalidOid;

	if (fc_sublevels_up)
		*fc_sublevels_up = 0;

	if (fc_schemaname != NULL)
	{
		Oid			fc_namespaceId;

		/*
		 * 我们可以在这里使用 LookupNamespaceNoError()，因为我们只对
		 * 找到现有的 RTE 感兴趣。检查模式的 USAGE 权限是没有必要的，
		 * 因为在创建 RTE 时已经检查过。此外，如果名称恰好
		 * 匹配用户没有权限访问的模式名称时，我们希望报告“没有找到 RTE”，
		 * 而不是“没有模式的权限”。
		 */
		fc_namespaceId = LookupNamespaceNoError(fc_schemaname);
		if (!OidIsValid(fc_namespaceId))
			return NULL;
		fc_relId = get_relname_relid(fc_refname, fc_namespaceId);
		if (!OidIsValid(fc_relId))
			return NULL;
	}

	while (fc_pstate != NULL)
	{
		ParseNamespaceItem *fc_result;

		if (OidIsValid(fc_relId))
			fc_result = fc_scanNameSpaceForRelid(fc_pstate, fc_relId, fc_location);
		else
			fc_result = fc_scanNameSpaceForRefname(fc_pstate, fc_refname, fc_location);

		if (fc_result)
			return fc_result;

		if (fc_sublevels_up)
			(*fc_sublevels_up)++;
		else
			break;

		fc_pstate = fc_pstate->parentParseState;
	}
	return NULL;
}

/*
 * 在查询的表命名空间中搜索与给定无限定 refname 匹配的项。
 * 如果是唯一匹配，则返回 nsitem；如果没有匹配，则返回 NULL。
 * 如果有多个匹配，则引发错误。
 *
 * 注意：看起来我们不应该担心有多个匹配的可能性；毕竟，SQL 标准不允许在给定 SELECT 级别内重复的表别名。
 * 然而，历史上，Postgres 的要求比这要宽松。例如，我们允许
 *		SELECT ... FROM tab1 x CROSS JOIN (tab2 x CROSS JOIN tab3 y) z
 * 理由是带别名的连接 (z) 隐藏了其中的别名，
 * 因此两个名为“x”的 RTE 之间没有冲突。然而，
 * 如果 tab3 是一个 LATERAL 子查询，那么在子查询中两个“x”都是可见的。
 * 我们并不拒绝以前可以使用的查询，而是允许这种情况，仅当实际存在对“x”的歧义引用时才会抱怨。
 */
static ParseNamespaceItem * fc_scanNameSpaceForRefname(ParseState *fc_pstate, const char *fc_refname, int fc_location)
{
	ParseNamespaceItem *fc_result = NULL;
	ListCell   *fc_l;

	foreach(fc_l, fc_pstate->p_namespace)
	{
		ParseNamespaceItem *fc_nsitem = (ParseNamespaceItem *) lfirst(fc_l);

		/* 忽略仅列项 */
		if (!fc_nsitem->p_rel_visible)
			continue;
		/* 如果不在 LATERAL 中，则忽略仅 LATERAL 项 */
		if (fc_nsitem->p_lateral_only && !fc_pstate->p_lateral_active)
			continue;

		if (strcmp(fc_nsitem->p_names->aliasname, fc_refname) == 0)
		{
			if (fc_result)
				ereport(ERROR,
						(errcode(ERRCODE_AMBIGUOUS_ALIAS),
						 errmsg("table reference \"%s\" is ambiguous",
								fc_refname),
						 parser_errposition(fc_pstate, fc_location)));
			fc_check_lateral_ref_ok(fc_pstate, fc_nsitem, fc_location);
			fc_result = fc_nsitem;
		}
	}
	return fc_result;
}

/*
 * 在查询的表命名空间中搜索与给定关系 OID 匹配的关系项。
 * 如果是唯一匹配，则返回 nsitem；如果没有匹配，则返回 NULL。
 * 如果有多个匹配，则引发错误。
 *
 * 请参阅 refnameNamespaceItem 的注释，以了解其为何表现如此。
 */
static ParseNamespaceItem * fc_scanNameSpaceForRelid(ParseState *fc_pstate, Oid fc_relid, int fc_location)
{
	ParseNamespaceItem *fc_result = NULL;
	ListCell   *fc_l;

	foreach(fc_l, fc_pstate->p_namespace)
	{
		ParseNamespaceItem *fc_nsitem = (ParseNamespaceItem *) lfirst(fc_l);
		RangeTblEntry *fc_rte = fc_nsitem->p_rte;

		/* 忽略仅列项 */
		if (!fc_nsitem->p_rel_visible)
			continue;
		/* 如果不在 LATERAL 中，则忽略仅 LATERAL 项 */
		if (fc_nsitem->p_lateral_only && !fc_pstate->p_lateral_active)
			continue;

		/* 是的，别名 == NULL 的测试应该存在... */
		if (fc_rte->rtekind == RTE_RELATION &&
			fc_rte->relid == fc_relid &&
			fc_rte->alias == NULL)
		{
			if (fc_result)
				ereport(ERROR,
						(errcode(ERRCODE_AMBIGUOUS_ALIAS),
						 errmsg("table reference %u is ambiguous",
								fc_relid),
						 parser_errposition(fc_pstate, fc_location)));
			fc_check_lateral_ref_ok(fc_pstate, fc_nsitem, fc_location);
			fc_result = fc_nsitem;
		}
	}
	return fc_result;
}

/*
 * 在查询的 CTE 命名空间中搜索与给定无限定 refname 匹配的 CTE。
 * 如果匹配，则返回 CTE（及其 levelsup 计数），如果没有匹配则返回 NULL。
 * 我们不必担心多个匹配，因为 parse_cte.c 拒绝包含重复 CTE 名称的 WITH 列表。
 */
CommonTableExpr * scanNameSpaceForCTE(ParseState *fc_pstate, const char *fc_refname,
					Index *fc_ctelevelsup)
{
	Index		fc_levelsup;

	for (fc_levelsup = 0;
		 fc_pstate != NULL;
		 fc_pstate = fc_pstate->parentParseState, fc_levelsup++)
	{
		ListCell   *fc_lc;

		foreach(fc_lc, fc_pstate->p_ctenamespace)
		{
			CommonTableExpr *fc_cte = (CommonTableExpr *) lfirst(fc_lc);

			if (strcmp(fc_cte->ctename, fc_refname) == 0)
			{
				*fc_ctelevelsup = fc_levelsup;
				return fc_cte;
			}
		}
	}
	return NULL;
}

/*
 * 搜索可能的“未来 CTE”，即根据 WITH 作用域规则尚不在作用域内的 CTE。
 * 这与有效的 SQL 语义无关，但对于错误报告目的很重要。
 */
static bool fc_isFutureCTE(ParseState *fc_pstate, const char *fc_refname)
{
	for (; fc_pstate != NULL; fc_pstate = fc_pstate->parentParseState)
	{
		ListCell   *fc_lc;

		foreach(fc_lc, fc_pstate->p_future_ctes)
		{
			CommonTableExpr *fc_cte = (CommonTableExpr *) lfirst(fc_lc);

			if (strcmp(fc_cte->ctename, fc_refname) == 0)
				return true;
		}
	}
	return false;
}

/*
 * 在查询的短暂命名关系命名空间中搜索与给定无限定 refname 匹配的关系。
 */
bool scanNameSpaceForENR(ParseState *fc_pstate, const char *fc_refname)
{
	return name_matches_visible_ENR(fc_pstate, fc_refname);
}


/*
 * searchRangeTableForRel
 *	  查看是否有任何 RangeTblEntry 可能与 RangeVar 匹配。
 *	  如果是，返回指向 RangeTblEntry 的指针；否则返回 NULL。
 *
 * 这与 refnameNamespaceItem 不同，因为它考虑了 ParseState 的 rangetable(s) 中的每个
 * 条目，而不仅仅是当前在 p_namespace 列表中可见的那些条目。根据 SQL 规范，这种行为
 * 是无效的，可能会给出模糊的结果（可能有多个同样有效的匹配，但只会返回一个）。这
 * 必须仅作为一种启发式方法来提供合适的错误消息。请参见 errorMissingRTE。
 *
 * 请注意，我们考虑了实际关系（或 CTE）名称的匹配和别名的匹配。
 */
static RangeTblEntry * fc_searchRangeTableForRel(ParseState *fc_pstate, RangeVar *fc_relation)
{
	const char *fc_refname = fc_relation->relname;
	Oid			fc_relId = InvalidOid;
	CommonTableExpr *fc_cte = NULL;
	bool		fc_isenr = false;
	Index		fc_ctelevelsup = 0;
	Index		fc_levelsup;

	/*
	 * 如果是一个不合格的名称，检查是否有可能的 CTE 匹配。CTE
	 * 隐藏了任何真正关系的匹配。如果没有 CTE，则寻找匹配的
	 * 关系。
	 *
	 * 注意：在面对并发 DDL 时，RangeVarGetRelid 在这里返回正确答案并不是关键。
	 * 如果没有返回正确答案，最糟糕的情况只是错误消息不够清晰。此外，查询中涉及的
	 * 表已经被锁定，这减少了可能发生惊人行为的情况。因此，我们在未锁定的状态下进行名称查找。
	 */
	if (!fc_relation->schemaname)
	{
		fc_cte = scanNameSpaceForCTE(fc_pstate, fc_refname, &fc_ctelevelsup);
		if (!fc_cte)
			fc_isenr = scanNameSpaceForENR(fc_pstate, fc_refname);
	}

	if (!fc_cte && !fc_isenr)
		fc_relId = RangeVarGetRelid(fc_relation, NoLock, true);

	/* 现在查找与关系/CTE/ENR 或别名匹配的 RTE */
	for (fc_levelsup = 0;
		 fc_pstate != NULL;
		 fc_pstate = fc_pstate->parentParseState, fc_levelsup++)
	{
		ListCell   *fc_l;

		foreach(fc_l, fc_pstate->p_rtable)
		{
			RangeTblEntry *fc_rte = (RangeTblEntry *) lfirst(fc_l);

			if (fc_rte->rtekind == RTE_RELATION &&
				OidIsValid(fc_relId) &&
				fc_rte->relid == fc_relId)
				return fc_rte;
			if (fc_rte->rtekind == RTE_CTE &&
				fc_cte != NULL &&
				fc_rte->ctelevelsup + fc_levelsup == fc_ctelevelsup &&
				strcmp(fc_rte->ctename, fc_refname) == 0)
				return fc_rte;
			if (fc_rte->rtekind == RTE_NAMEDTUPLESTORE &&
				fc_isenr &&
				strcmp(fc_rte->enrname, fc_refname) == 0)
				return fc_rte;
			if (strcmp(fc_rte->eref->aliasname, fc_refname) == 0)
				return fc_rte;
		}
	}
	return NULL;
}

/*
 * 检查两个命名空间列表之间的关系名称冲突。
 * 如果发现任何冲突则引发错误。
 *
 * 注意：我们假设每个给定的参数本身没有冲突；我们只想知道这两个是否可以合并。
 *
 * 根据 SQL，如果两个没有别名的普通关系 RTE 的 eref->aliasname 相同（即
 * 相同的关系名称），即使它们具有不同的关系 OID（暗示它们在不同的架构中），
 * 也不会发生冲突。
 *
 * 我们忽略命名空间项中的仅限横向的标志：即使所有项都被认为是可见的，这些列表
 * 也必须不冲突。然而，仅限列的项应该被忽略。
 */
void checkNameSpaceConflicts(ParseState *fc_pstate, List *fc_namespace1,
						List *fc_namespace2)
{
	ListCell   *fc_l1;

	foreach(fc_l1, fc_namespace1)
	{
		ParseNamespaceItem *fc_nsitem1 = (ParseNamespaceItem *) lfirst(fc_l1);
		RangeTblEntry *fc_rte1 = fc_nsitem1->p_rte;
		const char *fc_aliasname1 = fc_nsitem1->p_names->aliasname;
		ListCell   *fc_l2;

		if (!fc_nsitem1->p_rel_visible)
			continue;

		foreach(fc_l2, fc_namespace2)
		{
			ParseNamespaceItem *fc_nsitem2 = (ParseNamespaceItem *) lfirst(fc_l2);
			RangeTblEntry *fc_rte2 = fc_nsitem2->p_rte;
			const char *fc_aliasname2 = fc_nsitem2->p_names->aliasname;

			if (!fc_nsitem2->p_rel_visible)
				continue;
			if (strcmp(fc_aliasname2, fc_aliasname1) != 0)
				continue;		/* 绝对没有冲突 */
			if (fc_rte1->rtekind == RTE_RELATION && fc_rte1->alias == NULL &&
				fc_rte2->rtekind == RTE_RELATION && fc_rte2->alias == NULL &&
				fc_rte1->relid != fc_rte2->relid)
				continue;		/* 根据 SQL 规则没有冲突 */
			ereport(ERROR,
					(errcode(ERRCODE_DUPLICATE_ALIAS),
					 errmsg("table name \"%s\" specified more than once",
							fc_aliasname1)));
		}
	}
}

/*
 * 如果命名空间项当前被禁止作为 LATERAL 引用，则发出警告。
 * 这执行了 SQL:2008 对于在外连接的错误边的 LATERAL 引用的相当奇怪的想法，
 * 以及我们自己禁止在 FROM/USING 子句中引用 UPDATE 或 DELETE 的目标表作为
 * 横向引用的规定。
 *
 * 注意：pstate 应该与发现 nsitem 的相同查询级别。
 *
 * 便利的子例程以避免多次重复一个相当难看的 ereport。
 */
static void fc_check_lateral_ref_ok(ParseState *fc_pstate, ParseNamespaceItem *fc_nsitem,
					 int fc_location)
{
	if (fc_nsitem->p_lateral_only && !fc_nsitem->p_lateral_ok)
	{
		/* SQL:2008 要求这是一种错误，而不是不可见项 */
		RangeTblEntry *fc_rte = fc_nsitem->p_rte;
		char	   *fc_refname = fc_nsitem->p_names->aliasname;

		ereport(ERROR,
				(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
				 errmsg("invalid reference to FROM-clause entry for table \"%s\"",
						fc_refname),
				 (fc_pstate->p_target_nsitem != NULL &&
				  fc_rte == fc_pstate->p_target_nsitem->p_rte) ?
				 errhint("There is an entry for table \"%s\", but it cannot be referenced from this part of the query.",
						 fc_refname) :
				 errdetail("The combining JOIN type must be INNER or LEFT for a LATERAL reference."),
				 parser_errposition(fc_pstate, fc_location)));
	}
}

/*
 * 给定一个 RT 索引和嵌套深度，找到相应的
 * ParseNamespaceItem（一定有一个）。
 */
ParseNamespaceItem * GetNSItemByRangeTablePosn(ParseState *fc_pstate,
						  int fc_varno,
						  int fc_sublevels_up)
{
	ListCell   *fc_lc;

	while (fc_sublevels_up-- > 0)
	{
		fc_pstate = fc_pstate->parentParseState;
		Assert(fc_pstate != NULL);
	}
	foreach(fc_lc, fc_pstate->p_namespace)
	{
		ParseNamespaceItem *fc_nsitem = (ParseNamespaceItem *) lfirst(fc_lc);

		if (fc_nsitem->p_rtindex == fc_varno)
			return fc_nsitem;
	}
	elog(ERROR, "nsitem not found (internal error)");
	return NULL;				/* 保持编译器安静 */
}

/*
 * 给定一个 RT 索引和嵌套深度，找到相应的 RTE。
 * （注意，RTE 不必在查询的命名空间中。）
 */
RangeTblEntry *
GetRTEByRangeTablePosn(ParseState *fc_pstate,
					   int fc_varno,
					   int fc_sublevels_up)
{
	while (fc_sublevels_up-- > 0)
	{
		fc_pstate = fc_pstate->parentParseState;
		Assert(fc_pstate != NULL);
	}
	Assert(fc_varno > 0 && fc_varno <= list_length(fc_pstate->p_rtable));
	return rt_fetch(fc_varno, fc_pstate->p_rtable);
}

/*
 * 获取CTE引用RTE的CTE。
 *
 * rtelevelsup是给定pstate以上的查询层级中该RTE的级别数。
 */
CommonTableExpr * GetCTEForRTE(ParseState *fc_pstate, RangeTblEntry *fc_rte, int fc_rtelevelsup)
{
	Index		fc_levelsup;
	ListCell   *fc_lc;

	Assert(fc_rte->rtekind == RTE_CTE);
	fc_levelsup = fc_rte->ctelevelsup + fc_rtelevelsup;
	while (fc_levelsup-- > 0)
	{
		fc_pstate = fc_pstate->parentParseState;
		if (!fc_pstate)			/* 不应该发生 */
			elog(ERROR, "bad levelsup for CTE \"%s\"", fc_rte->ctename);
	}
	foreach(fc_lc, fc_pstate->p_ctenamespace)
	{
		CommonTableExpr *fc_cte = (CommonTableExpr *) lfirst(fc_lc);

		if (strcmp(fc_cte->ctename, fc_rte->ctename) == 0)
			return fc_cte;
	}
	/* 不应该发生 */
	elog(ERROR, "could not find CTE \"%s\"", fc_rte->ctename);
	return NULL;				/* 保持编译器安静 */
}

/*
 * updateFuzzyAttrMatchState
 *	  使用Levenshtein距离，考虑该列是否为最佳模糊匹配。
 */
static void fc_updateFuzzyAttrMatchState(int fc_fuzzy_rte_penalty,
						  FuzzyAttrMatchState *fc_fuzzystate, RangeTblEntry *fc_rte,
						  const char *fc_actual, const char *fc_match, int fc_attnum)
{
	int			fc_columndistance;
	int			fc_matchlen;

	/* 如果没有希望，计算Levenshtein距离之前提前退出。 */
	if (fc_fuzzy_rte_penalty > fc_fuzzystate->distance)
		return;

	/*
	 * 明确拒绝已删除的列，这些列在scanRTEForColumn()中可能会以明显的
	 * 空实际名称出现。
	 */
	if (fc_actual[0] == '\0')
		return;

	/* 使用Levenshtein来计算匹配距离。 */
	fc_matchlen = strlen(fc_match);
	fc_columndistance =
		varstr_levenshtein_less_equal(fc_actual, strlen(fc_actual), fc_match, fc_matchlen,
									  1, 1, 1,
									  fc_fuzzystate->distance + 1
									  - fc_fuzzy_rte_penalty,
									  true);

	/*
	 * 如果超过一半的字符不同，不要将其视为匹配，以避免提出荒谬的建议。
	 */
	if (fc_columndistance > fc_matchlen / 2)
		return;

	/*
	 * 从这一点开始，我们可以忽略RTE名称距离与列名称距离之间的区别。
	 */
	fc_columndistance += fc_fuzzy_rte_penalty;

	/*
	 * 如果新的距离小于或等于迄今为止找到的最佳匹配，更新fuzzystate。
	 */
	if (fc_columndistance < fc_fuzzystate->distance)
	{
		/* 为RTE存储新的最低观测距离 */
		fc_fuzzystate->distance = fc_columndistance;
		fc_fuzzystate->rfirst = fc_rte;
		fc_fuzzystate->first = fc_attnum;
		fc_fuzzystate->rsecond = NULL;
		fc_fuzzystate->second = InvalidAttrNumber;
	}
	else if (fc_columndistance == fc_fuzzystate->distance)
	{
		/*
		 * 该匹配距离可能等于该范围表中之前的匹配。当发生这种情况时，之前的匹配
		 * 也可以被给予，但只有在RTE中距离相等的匹配没有超过两个的情况下（
		 * 反过来，我们的调用者只接受总体上两个相等距离的匹配）。
		 */
		if (AttributeNumberIsValid(fc_fuzzystate->second))
		{
			/* RTE级别匹配过多 */
			fc_fuzzystate->rfirst = NULL;
			fc_fuzzystate->first = InvalidAttrNumber;
			fc_fuzzystate->rsecond = NULL;
			fc_fuzzystate->second = InvalidAttrNumber;
			/* 显然，距离对于*任何* RTE来说限制太低 */
			fc_fuzzystate->distance = fc_columndistance - 1;
		}
		else if (AttributeNumberIsValid(fc_fuzzystate->first))
		{
			/* 记录为RTE的临时第二匹配 */
			fc_fuzzystate->rsecond = fc_rte;
			fc_fuzzystate->second = fc_attnum;
		}
		else if (fc_fuzzystate->distance <= MAX_FUZZY_DISTANCE)
		{
			/*
			 * 记录为临时第一个匹配（这可能偶尔发生，因为先前的最低距离
			 * “限制太低”，而不是与真实匹配相关）。
			 */
			fc_fuzzystate->rfirst = fc_rte;
			fc_fuzzystate->first = fc_attnum;
		}
	}
}

/*
 * scanNSItemForColumn
 *	  搜索单个命名空间项的列名以找到给定名称。
 *	  如果找到，返回相应的Var节点，否则返回NULL。
 *	  如果名称在该nsitem中证明模糊，则引发错误。
 *
 * 副作用：如果找到匹配，则将相应的RTE标记为需要对该列的读取访问权限。
 */
Node * scanNSItemForColumn(ParseState *fc_pstate, ParseNamespaceItem *fc_nsitem,
					int fc_sublevels_up, const char *fc_colname, int fc_location)
{
	RangeTblEntry *fc_rte = fc_nsitem->p_rte;
	int			fc_attnum;
	Var		   *fc_var;

	/*
	 * 扫描nsitem的列名（或别名）以寻找匹配。如果有多个匹配则投诉。
	 */
	fc_attnum = fc_scanRTEForColumn(fc_pstate, fc_rte, fc_nsitem->p_names,
							  fc_colname, fc_location,
							  0, NULL);

	if (fc_attnum == InvalidAttrNumber)
		return NULL;			/* 如果没有匹配则返回NULL */

	/* 在约束检查中，除了tableOid外不允许有系统列 */
	if (fc_pstate->p_expr_kind == EXPR_KIND_CHECK_CONSTRAINT &&
		fc_attnum < InvalidAttrNumber && fc_attnum != TableOidAttributeNumber)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
				 errmsg("system column \"%s\" reference in check constraint is invalid",
						fc_colname),
				 parser_errposition(fc_pstate, fc_location)));

	/* 在生成列中，除了tableOid外不允许有系统列 */
	if (fc_pstate->p_expr_kind == EXPR_KIND_GENERATED_COLUMN &&
		fc_attnum < InvalidAttrNumber && fc_attnum != TableOidAttributeNumber)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
				 errmsg("cannot use system column \"%s\" in column generation expression",
						fc_colname),
				 parser_errposition(fc_pstate, fc_location)));

	/*
	 * 在MERGE WHEN条件中，除了tableOid外不允许有系统列
	 */
	if (fc_pstate->p_expr_kind == EXPR_KIND_MERGE_WHEN &&
		fc_attnum < InvalidAttrNumber && fc_attnum != TableOidAttributeNumber)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
				 errmsg("cannot use system column \"%s\" in MERGE WHEN condition",
						fc_colname),
				 parser_errposition(fc_pstate, fc_location)));

	/* 找到有效匹配，因此构建一个Var */
	if (fc_attnum > InvalidAttrNumber)
	{
		/* 从ParseNamespaceColumn数组中获取属性数据 */
		ParseNamespaceColumn *fc_nscol = &fc_nsitem->p_nscolumns[fc_attnum - 1];

		/* 如果列已删除则投诉。请查看scanRTEForColumn中的说明。 */
		if (fc_nscol->p_varno == 0)
			ereport(ERROR,
					(errcode(ERRCODE_UNDEFINED_COLUMN),
					 errmsg("column \"%s\" of relation \"%s\" does not exist",
							fc_colname,
							fc_nsitem->p_names->aliasname)));

		fc_var = makeVar(fc_nscol->p_varno,
					  fc_nscol->p_varattno,
					  fc_nscol->p_vartype,
					  fc_nscol->p_vartypmod,
					  fc_nscol->p_varcollid,
					  fc_sublevels_up);
		/* makeVar没有提供这些的参数，因此手动设置： */
		fc_var->varnosyn = fc_nscol->p_varnosyn;
		fc_var->varattnosyn = fc_nscol->p_varattnosyn;
	}
	else
	{
		/* 系统列，因此使用预定类型数据 */
		const FormData_pg_attribute *fc_sysatt;

		fc_sysatt = SystemAttributeDefinition(fc_attnum);
		fc_var = makeVar(fc_nsitem->p_rtindex,
					  fc_attnum,
					  fc_sysatt->atttypid,
					  fc_sysatt->atttypmod,
					  fc_sysatt->attcollation,
					  fc_sublevels_up);
	}
	fc_var->location = fc_location;

	/* 需要对该列的读取访问权限 */
	markVarForSelectPriv(fc_pstate, fc_var);

	return (Node *) fc_var;
}

/*
 * scanRTEForColumn
 *	  搜索单个RTE的列名以找到给定名称。
 *	  如果找到，返回attnum（可能为负数，表示系统列）；
 *	  否则返回InvalidAttrNumber。
 *	  如果名称在该RTE中证明模糊，则引发错误。
 *
 * 实际上，我们只搜索“eref”中列出的名称。这可以是
 * rte->eref，在这种情况下我们确实在搜索所有的列名，
 * 或对于连接，它可以是rte->join_using_alias，在这种情况下我们只考虑
 * 公共列名（这些是连接的前N列，因此一切正常）。
 *
 * pstate和location仅用于错误报告目的。
 *
 * 副作用：如果fuzzystate非NULL，检查非系统列
 * 是否有近似匹配，并相应地更新fuzzystate。
 *
 * 注意：这是分离出来的scanNSItemForColumn，因为错误消息
 * 创建可能希望检查不在命名空间中的RTE。为了支持这
 * 一用法，尽量减少在这里执行的有效性检查次数。
 * 不过，对于模糊名称的情况抱怨是可以的，因为如果我们
 * 正在处理无效名称时，我们已经消除了这一点。
 */
static int fc_scanRTEForColumn(ParseState *fc_pstate, RangeTblEntry *fc_rte,
				 Alias *fc_eref,
				 const char *fc_colname, int fc_location,
				 int fc_fuzzy_rte_penalty,
				 FuzzyAttrMatchState *fc_fuzzystate)
{
	int			fc_result = InvalidAttrNumber;
	int			fc_attnum = 0;
	ListCell   *fc_c;

	/*
	 * 扫描用户列名（或别名）以寻找匹配。如果有多个匹配则投诉。
	 *
	 * 注意：eref->colnames可能包括已删除列的条目，但那些
	 * 将是无法匹配任何合法SQL标识符的空字符串，因此我们
	 * 不在这里测试这种情况。
	 *
	 * 如果这 somehow 出了问题，我们试图访问一个已删除列，
	 * 我们依然会通过在scanNSItemForColumn()中的检查捕获它。
	 * 不过，想要找到最短距离匹配的调用者需要直接对此进行防范。
	 */
	foreach(fc_c, fc_eref->colnames)
	{
		const char *fc_attcolname = strVal(lfirst(fc_c));

		fc_attnum++;
		if (strcmp(fc_attcolname, fc_colname) == 0)
		{
			if (fc_result)
				ereport(ERROR,
						(errcode(ERRCODE_AMBIGUOUS_COLUMN),
						 errmsg("column reference \"%s\" is ambiguous",
								fc_colname),
						 parser_errposition(fc_pstate, fc_location)));
			fc_result = fc_attnum;
		}

		/* 如果提供，更新模糊匹配状态。 */
		if (fc_fuzzystate != NULL)
			fc_updateFuzzyAttrMatchState(fc_fuzzy_rte_penalty, fc_fuzzystate,
									  fc_rte, fc_attcolname, fc_colname, fc_attnum);
	}

	/*
	 * 如果我们有一个唯一的匹配，返回它。注意，这允许用户
	 * 别名覆盖系统列名（例如 OID），而不会出现错误。
	 */
	if (fc_result)
		return fc_result;

	/*
	 * 如果 RTE 代表一个真实的关系，则考虑系统列名。
	 * 复合体仅用于类似于 ON CONFLICT 的伪关系
	 * 被排除项。
	 */
	if (fc_rte->rtekind == RTE_RELATION &&
		fc_rte->relkind != RELKIND_COMPOSITE_TYPE)
	{
		/* 快速检查名称是否可能是系统列 */
		fc_attnum = fc_specialAttNum(fc_colname);
		if (fc_attnum != InvalidAttrNumber)
		{
			/* 现在检查该列是否实际上是定义的 */
			if (SearchSysCacheExists2(ATTNUM,
									  ObjectIdGetDatum(fc_rte->relid),
									  Int16GetDatum(fc_attnum)))
				fc_result = fc_attnum;
		}
	}

	return fc_result;
}

/*
 * colNameToVar
 *	  搜索未限定的列名。
 *	  如果找到，返回相应的 Var 节点（或表达式）。
 *	  如果未找到，则返回 NULL。如果名称被证明模糊，则引发错误。
 *	  如果 localonly 为 true，则仅考虑最内层查询中的名称。
 */
Node * colNameToVar(ParseState *fc_pstate, const char *fc_colname, bool fc_localonly,
			 int fc_location)
{
	Node	   *fc_result = NULL;
	int			fc_sublevels_up = 0;
	ParseState *fc_orig_pstate = fc_pstate;

	while (fc_pstate != NULL)
	{
		ListCell   *fc_l;

		foreach(fc_l, fc_pstate->p_namespace)
		{
			ParseNamespaceItem *fc_nsitem = (ParseNamespaceItem *) lfirst(fc_l);
			Node	   *fc_newresult;

			/* 忽略仅表项 */
			if (!fc_nsitem->p_cols_visible)
				continue;
			/* 如果不在 LATERAL 中，则忽略仅 LATERAL 项 */
			if (fc_nsitem->p_lateral_only && !fc_pstate->p_lateral_active)
				continue;

			/* 在此处使用 orig_pstate 以保持与其他调用者的一致性 */
			fc_newresult = scanNSItemForColumn(fc_orig_pstate, fc_nsitem, fc_sublevels_up,
											fc_colname, fc_location);

			if (fc_newresult)
			{
				if (fc_result)
					ereport(ERROR,
							(errcode(ERRCODE_AMBIGUOUS_COLUMN),
							 errmsg("column reference \"%s\" is ambiguous",
									fc_colname),
							 parser_errposition(fc_pstate, fc_location)));
				fc_check_lateral_ref_ok(fc_pstate, fc_nsitem, fc_location);
				fc_result = fc_newresult;
			}
		}

		if (fc_result != NULL || fc_localonly)
			break;				/* 找到，或不想查看父项 */

		fc_pstate = fc_pstate->parentParseState;
		fc_sublevels_up++;
	}

	return fc_result;
}

/*
 * searchRangeTableForCol
 *	  查看任何 RangeTblEntry 是否可能提供给定的列名（或
 *	  找到可用的最佳匹配）。返回包含相关详细信息的状态。
 *
 * 这与 colNameToVar 不同，因为它考虑了 ParseState 的每个条目
 * rangetable（不仅仅是当前在 p_namespace 列表中可见的条目）。这种行为在 SQL 规范中无效，
 * 可能会给出模糊的结果（可能有多个同样有效的匹配，但只有一个会被返回）。这必须仅作为
 * 提供合适错误消息的启发式方法。请参阅 errorMissingColumn。
 *
 * 此函数还不同，因为它将考虑近似
 * 匹配 - 如果用户输入的别名/列对与有效对仅略有不同，
 * 我们可能能够推断他们想要输入的内容并提供合理的提示。
 *
 * FuzzyAttrMatchState 将有 'rfirst' 指向包含别名和列名最有希望匹配的最佳 RTE。
 * 如果别名和列名完全匹配，则 'first' 将是 InvalidAttrNumber；
 * 否则，它将是匹配的属性编号。在后一种情况下，'rsecond' 可能指向第二个同样接近的近似匹配，
 * 而 'second' 将包含第二个匹配的属性编号。
 */
static FuzzyAttrMatchState *
fc_searchRangeTableForCol(ParseState *fc_pstate, const char *fc_alias, const char *fc_colname,
					   int fc_location)
{
	ParseState *fc_orig_pstate = fc_pstate;
	FuzzyAttrMatchState *fc_fuzzystate = palloc(sizeof(FuzzyAttrMatchState));

	fc_fuzzystate->distance = MAX_FUZZY_DISTANCE + 1;
	fc_fuzzystate->rfirst = NULL;
	fc_fuzzystate->rsecond = NULL;
	fc_fuzzystate->first = InvalidAttrNumber;
	fc_fuzzystate->second = InvalidAttrNumber;

	while (fc_pstate != NULL)
	{
		ListCell   *fc_l;

		foreach(fc_l, fc_pstate->p_rtable)
		{
			RangeTblEntry *fc_rte = (RangeTblEntry *) lfirst(fc_l);
			int			fc_fuzzy_rte_penalty = 0;

			/*
			 * 通常，在连接 RTE 中查找匹配项并没有用；
			 * 对于我们的目的，它们有效地复制了其他 RTE，
			 * 如果从连接 RTE 中选择匹配项，最终诊断消息中将显示无帮助的别名。
			 */
			if (fc_rte->rtekind == RTE_JOIN)
				continue;

			/*
			 * 如果用户没有指定别名，则一个 RTE 的匹配项和另一个 RTE 一样好。
			 * 但是如果用户确实指定了别名，那么我们希望至少有一个模糊 - 最好是一个精确
			 * - 匹配范围表条目。
			 */
			if (fc_alias != NULL)
				fc_fuzzy_rte_penalty =
					varstr_levenshtein_less_equal(fc_alias, strlen(fc_alias),
												  fc_rte->eref->aliasname,
												  strlen(fc_rte->eref->aliasname),
												  1, 1, 1,
												  MAX_FUZZY_DISTANCE + 1,
												  true);

			/*
			 * 扫描以寻找匹配的列；如果找到精确匹配，我们就完成了。
			 * 否则，更新 fuzzystate。
			 */
			if (fc_scanRTEForColumn(fc_orig_pstate, fc_rte, fc_rte->eref, fc_colname, fc_location,
								 fc_fuzzy_rte_penalty, fc_fuzzystate)
				&& fc_fuzzy_rte_penalty == 0)
			{
				fc_fuzzystate->rfirst = fc_rte;
				fc_fuzzystate->first = InvalidAttrNumber;
				fc_fuzzystate->rsecond = NULL;
				fc_fuzzystate->second = InvalidAttrNumber;
				return fc_fuzzystate;
			}
		}

		fc_pstate = fc_pstate->parentParseState;
	}

	return fc_fuzzystate;
}

/*
 * markRTEForSelectPriv
 *	   将索引 rtindex 的 RTE 中指定的列标记为需要 SELECT 特权
 *
 * col == InvalidAttrNumber 表示“整行”引用
 */
static void fc_markRTEForSelectPriv(ParseState *fc_pstate, int fc_rtindex, AttrNumber fc_col)
{
	RangeTblEntry *fc_rte = rt_fetch(fc_rtindex, fc_pstate->p_rtable);

	if (fc_rte->rtekind == RTE_RELATION)
	{
		/* 确保整个关系被标记为可执行 SELECT 访问 */
		fc_rte->requiredPerms |= ACL_SELECT;
		/* 必须偏移 attnum 以适应位图集 */
		fc_rte->selectedCols = bms_add_member(fc_rte->selectedCols,
										   fc_col - FirstLowInvalidHeapAttributeNumber);
	}
	else if (fc_rte->rtekind == RTE_JOIN)
	{
		if (fc_col == InvalidAttrNumber)
		{
			/*
			 * 对于连接的整行引用，必须视为对两个输入的整行引用。
			 */
			JoinExpr   *fc_j;

			if (fc_rtindex > 0 && fc_rtindex <= list_length(fc_pstate->p_joinexprs))
				fc_j = list_nth_node(JoinExpr, fc_pstate->p_joinexprs, fc_rtindex - 1);
			else
				fc_j = NULL;
			if (fc_j == NULL)
				elog(ERROR, "could not find JoinExpr for whole-row reference");

			/* 注意：我们在此处无法看到 FromExpr */
			if (IsA(fc_j->larg, RangeTblRef))
			{
				int			fc_varno = ((RangeTblRef *) fc_j->larg)->rtindex;

				fc_markRTEForSelectPriv(fc_pstate, fc_varno, InvalidAttrNumber);
			}
			else if (IsA(fc_j->larg, JoinExpr))
			{
				int			fc_varno = ((JoinExpr *) fc_j->larg)->rtindex;

				fc_markRTEForSelectPriv(fc_pstate, fc_varno, InvalidAttrNumber);
			}
			else
				elog(ERROR, "unrecognized node type: %d",
					 (int) nodeTag(fc_j->larg));
			if (IsA(fc_j->rarg, RangeTblRef))
			{
				int			fc_varno = ((RangeTblRef *) fc_j->rarg)->rtindex;

				fc_markRTEForSelectPriv(fc_pstate, fc_varno, InvalidAttrNumber);
			}
			else if (IsA(fc_j->rarg, JoinExpr))
			{
				int			fc_varno = ((JoinExpr *) fc_j->rarg)->rtindex;

				fc_markRTEForSelectPriv(fc_pstate, fc_varno, InvalidAttrNumber);
			}
			else
				elog(ERROR, "unrecognized node type: %d",
					 (int) nodeTag(fc_j->rarg));
		}
		else
		{
			/*
			 * 用于普通列的连接别名 Vars 必须引用合并的 JOIN
			 * USING 列。我们在这里不需要做任何事情，因为连接输入列
			 * 也将在连接的 qual 子句中被引用，并将在那里标记为选择特权。
			 */
		}
	}
	/* 其他 RTE 类型不需要特权标记 */
}

/*
 * markVarForSelectPriv
 *	   标记 Var 引用的 RTE 需要 Var 列的 SELECT 特权
 *	   （Var 也可以是整行 Var）
 */
void markVarForSelectPriv(ParseState *fc_pstate, Var *fc_var)
{
	Index		fc_lv;

	Assert(IsA(fc_var, Var));
	/* 如果是 uplevel Var，请找到适当的 pstate */
	for (fc_lv = 0; fc_lv < fc_var->varlevelsup; fc_lv++)
		fc_pstate = fc_pstate->parentParseState;
	fc_markRTEForSelectPriv(fc_pstate, fc_var->varno, fc_var->varattno);
}

/*
 * buildRelationAliases
 *		构建关系 RTE 的 eref 列名列表。
 *		此代码也用于函数 RTE。
 *
 * tupdesc：物理列信息
 * alias：用户提供的别名，如果没有则为 NULL
 * eref：用于存储列名的 eref 别名
 *
 * 充填 eref->colnames。此外，alias->colnames 也会重建以插入
 * 任何删除列的空字符串，以便与物理列编号一一对应。
 *
 * 存在的别名多于所需的将是一个错误。
 */
static void fc_buildRelationAliases(TupleDesc fc_tupdesc, Alias *fc_alias, Alias *fc_eref)
{
	int			fc_maxattrs = fc_tupdesc->natts;
	List	   *fc_aliaslist;
	ListCell   *fc_aliaslc;
	int			fc_numaliases;
	int			fc_varattno;
	int			fc_numdropped = 0;

	Assert(fc_eref->colnames == NIL);

	if (fc_alias)
	{
		fc_aliaslist = fc_alias->colnames;
		fc_aliaslc = list_head(fc_aliaslist);
		fc_numaliases = list_length(fc_aliaslist);
		/* 我们将重建别名列名列表 */
		fc_alias->colnames = NIL;
	}
	else
	{
		fc_aliaslist = NIL;
		fc_aliaslc = NULL;
		fc_numaliases = 0;
	}

	for (fc_varattno = 0; fc_varattno < fc_maxattrs; fc_varattno++)
	{
		Form_pg_attribute fc_attr = TupleDescAttr(fc_tupdesc, fc_varattno);
		String	   *fc_attrname;

		if (fc_attr->attisdropped)
		{
			/* 为已删除的列始终插入一个空字符串 */
			fc_attrname = makeString(pstrdup(""));
			if (fc_aliaslc)
				fc_alias->colnames = lappend(fc_alias->colnames, fc_attrname);
			fc_numdropped++;
		}
		else if (fc_aliaslc)
		{
			/* 使用下一个用户提供的别名 */
			fc_attrname = lfirst_node(String, fc_aliaslc);
			fc_aliaslc = lnext(fc_aliaslist, fc_aliaslc);
			fc_alias->colnames = lappend(fc_alias->colnames, fc_attrname);
		}
		else
		{
			fc_attrname = makeString(pstrdup(NameStr(fc_attr->attname)));
			/* 如果有别名，我们已经完成 */
		}

		fc_eref->colnames = lappend(fc_eref->colnames, fc_attrname);
	}

	/* 用户提供的别名太多？ */
	if (fc_aliaslc)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
				 errmsg("table \"%s\" has %d columns available but %d columns specified",
						fc_eref->aliasname, fc_maxattrs - fc_numdropped, fc_numaliases)));
}


/*
 * chooseScalarFunctionAlias
 *		选择一个函数在函数 RTE 中的列别名，
 *		当函数返回标量类型时（不是复合或 RECORD）。
 *
 * funcexpr: 函数调用的转换表达式树
 * funcname: 函数名称（由 FigureColname 确定）
 * alias: 用户提供的 RTE 别名，如果没有则为 NULL
 * nfuncs: 函数 RTE 中出现的函数数量
 *
 * 请注意，我们选择的名称可能会在稍后被覆盖，如果用户提供的
 * 别名包括列别名。 这在这里并不重要。
 */
static char * fc_chooseScalarFunctionAlias(Node *fc_funcexpr, char *fc_funcname,
						  Alias *fc_alias, int fc_nfuncs)
{
	char	   *fc_pname;

	/*
	 * 如果表达式是一个简单的函数调用，并且该函数有一个
	 * 命名的单 OUT 参数，则使用该参数的名称。
	 */
	if (fc_funcexpr && IsA(fc_funcexpr, FuncExpr))
	{
		fc_pname = get_func_result_name(((FuncExpr *) fc_funcexpr)->funcid);
		if (fc_pname)
			return fc_pname;
	}

	/*
	 * 如果 RTE 中只有一个函数，并且用户提供了一个 RTE 别名
	 * 名称，则使用该名称。 （这使得 FROM func() AS foo 使用 "foo" 作为
	 * 列名以及表别名。）
	 */
	if (fc_nfuncs == 1 && fc_alias)
		return fc_alias->aliasname;

	/*
	 * 否则使用函数名称。
	 */
	return fc_funcname;
}


/*
 * buildNSItemFromTupleDesc
 *		根据描述列的 tupdesc 构建一个 ParseNamespaceItem。
 *
 * rte: 新的 RangeTblEntry 对于该关系
 * rtindex: 在 rangetable 列表中的索引
 * tupdesc: 物理列信息
 */
static ParseNamespaceItem * fc_buildNSItemFromTupleDesc(RangeTblEntry *fc_rte, Index fc_rtindex, TupleDesc fc_tupdesc)
{
	ParseNamespaceItem *fc_nsitem;
	ParseNamespaceColumn *fc_nscolumns;
	int			fc_maxattrs = fc_tupdesc->natts;
	int			fc_varattno;

	/* colnames 必须与 nsitem 的条目数相同 */
	Assert(fc_maxattrs == list_length(fc_rte->eref->colnames));

	/* 从 tupdesc 中提取每列数据 */
	fc_nscolumns = (ParseNamespaceColumn *)
		palloc0(fc_maxattrs * sizeof(ParseNamespaceColumn));

	for (fc_varattno = 0; fc_varattno < fc_maxattrs; fc_varattno++)
	{
		Form_pg_attribute fc_attr = TupleDescAttr(fc_tupdesc, fc_varattno);

		/* 对于已删除的列，只需将条目留为零 */
		if (fc_attr->attisdropped)
			continue;

		fc_nscolumns[fc_varattno].p_varno = fc_rtindex;
		fc_nscolumns[fc_varattno].p_varattno = fc_varattno + 1;
		fc_nscolumns[fc_varattno].p_vartype = fc_attr->atttypid;
		fc_nscolumns[fc_varattno].p_vartypmod = fc_attr->atttypmod;
		fc_nscolumns[fc_varattno].p_varcollid = fc_attr->attcollation;
		fc_nscolumns[fc_varattno].p_varnosyn = fc_rtindex;
		fc_nscolumns[fc_varattno].p_varattnosyn = fc_varattno + 1;
	}

	/* ...并构建 nsitem */
	fc_nsitem = (ParseNamespaceItem *) palloc(sizeof(ParseNamespaceItem));
	fc_nsitem->p_names = fc_rte->eref;
	fc_nsitem->p_rte = fc_rte;
	fc_nsitem->p_rtindex = fc_rtindex;
	fc_nsitem->p_nscolumns = fc_nscolumns;
	/* 设置默认可见性标志；可能稍后会更改 */
	fc_nsitem->p_rel_visible = true;
	fc_nsitem->p_cols_visible = true;
	fc_nsitem->p_lateral_only = false;
	fc_nsitem->p_lateral_ok = true;

	return fc_nsitem;
}

/*
 * buildNSItemFromLists
 *		根据列表中的列类型信息构建一个 ParseNamespaceItem。
 *
 * rte: 新的 RangeTblEntry 对于该关系
 * rtindex: 在 rangetable 列表中的索引
 * coltypes: 每列的数据类型 OIDs
 * coltypmods: 每列的类型修饰符
 * colcollation: 每列的排序 OIDs
 */
static ParseNamespaceItem * fc_buildNSItemFromLists(RangeTblEntry *fc_rte, Index fc_rtindex,
					 List *fc_coltypes, List *fc_coltypmods, List *fc_colcollations)
{
	ParseNamespaceItem *fc_nsitem;
	ParseNamespaceColumn *fc_nscolumns;
	int			fc_maxattrs = list_length(fc_coltypes);
	int			fc_varattno;
	ListCell   *fc_lct;
	ListCell   *fc_lcm;
	ListCell   *fc_lcc;

	/* colnames 必须与 nsitem 的条目数相同 */
	Assert(fc_maxattrs == list_length(fc_rte->eref->colnames));

	Assert(fc_maxattrs == list_length(fc_coltypmods));
	Assert(fc_maxattrs == list_length(fc_colcollations));

	/* 从列表中提取每列数据 */
	fc_nscolumns = (ParseNamespaceColumn *)
		palloc0(fc_maxattrs * sizeof(ParseNamespaceColumn));

	fc_varattno = 0;
	forthree(fc_lct, fc_coltypes,
			 fc_lcm, fc_coltypmods,
			 fc_lcc, fc_colcollations)
	{
		fc_nscolumns[fc_varattno].p_varno = fc_rtindex;
		fc_nscolumns[fc_varattno].p_varattno = fc_varattno + 1;
		fc_nscolumns[fc_varattno].p_vartype = lfirst_oid(fc_lct);
		fc_nscolumns[fc_varattno].p_vartypmod = lfirst_int(fc_lcm);
		fc_nscolumns[fc_varattno].p_varcollid = lfirst_oid(fc_lcc);
		fc_nscolumns[fc_varattno].p_varnosyn = fc_rtindex;
		fc_nscolumns[fc_varattno].p_varattnosyn = fc_varattno + 1;
		fc_varattno++;
	}

	/* ...并构建 nsitem */
	fc_nsitem = (ParseNamespaceItem *) palloc(sizeof(ParseNamespaceItem));
	fc_nsitem->p_names = fc_rte->eref;
	fc_nsitem->p_rte = fc_rte;
	fc_nsitem->p_rtindex = fc_rtindex;
	fc_nsitem->p_nscolumns = fc_nscolumns;
	/* 设置默认可见性标志；可能稍后会更改 */
	fc_nsitem->p_rel_visible = true;
	fc_nsitem->p_cols_visible = true;
	fc_nsitem->p_lateral_only = false;
	fc_nsitem->p_lateral_ok = true;

	return fc_nsitem;
}

/*
 * 在解析分析期间打开一个表
 *
 * 这基本上与 table_openrv() 相同，只是它满足一些特定于解析器的错误报告需求，
 * 特别是它安排在任何结果错误中包含 RangeVar 的解析位置。
 *
 * 注意: 正确地说，lockmode 应该声明为 LOCKMODE 而不是 int，但这
 * 需要将 storage/lock.h 导入到 parse_relation.h 中。由于
 * LOCKMODE 仍然是 typedef 为 int，因此这似乎是多此一举。
 */
Relation parserOpenTable(ParseState *fc_pstate, const RangeVar *fc_relation, int fc_lockmode)
{
	Relation	fc_rel;
	ParseCallbackState fc_pcbstate;

	setup_parser_errposition_callback(&fc_pcbstate, fc_pstate, fc_relation->location);
	fc_rel = table_openrv_extended(fc_relation, fc_lockmode, true);
	if (fc_rel == NULL)
	{
		if (fc_relation->schemaname)
			ereport(ERROR,
					(errcode(ERRCODE_UNDEFINED_TABLE),
					 errmsg("relation \"%s.%s\" does not exist",
							fc_relation->schemaname, fc_relation->relname)));
		else
		{
			/*
			 * 可能是对某个尚未在作用域内的 CTE 的不合格名称。
			 * 纯粹的“不存在”消息在解决此类问题时证明了出奇的无用，
			 * 所以我们特意提供了具体的提示。
			 */
			if (fc_isFutureCTE(fc_pstate, fc_relation->relname))
				ereport(ERROR,
						(errcode(ERRCODE_UNDEFINED_TABLE),
						 errmsg("relation \"%s\" does not exist",
								fc_relation->relname),
						 errdetail("There is a WITH item named \"%s\", but it cannot be referenced from this part of the query.",
								   fc_relation->relname),
						 errhint("Use WITH RECURSIVE, or re-order the WITH items to remove forward references.")));
			else
				ereport(ERROR,
						(errcode(ERRCODE_UNDEFINED_TABLE),
						 errmsg("relation \"%s\" does not exist",
								fc_relation->relname)));
		}
	}
	cancel_parser_errposition_callback(&fc_pcbstate);
	return fc_rel;
}

/*
 * 为关系添加一个条目到 pstate 的范围表 (p_rtable)。
 * 然后，构造并返回一个新的 RTE 的 ParseNamespaceItem。
 *
 * 我们在这里不将 ParseNamespaceItem 链接到 pstate; 调用者的工作是在适当的方式下做到这一点。
 *
 * 注意: 以前这一部分检查了 refname 冲突，但这是错误的。
 * 调用者负责在适当的作用域内检查冲突。
 */
ParseNamespaceItem * addRangeTableEntry(ParseState *fc_pstate,
				   RangeVar *fc_relation,
				   Alias *fc_alias,
				   bool fc_inh,
				   bool fc_inFromCl)
{
	RangeTblEntry *fc_rte = makeNode(RangeTblEntry);
	char	   *fc_refname = fc_alias ? fc_alias->aliasname : fc_relation->relname;
	LOCKMODE	fc_lockmode;
	Relation	fc_rel;
	ParseNamespaceItem *fc_nsitem;

	Assert(fc_pstate != NULL);

	fc_rte->rtekind = RTE_RELATION;
	fc_rte->alias = fc_alias;

	/*
	 * 确定我们对该关系所需的锁的类型。它不是查询的目标表
	 * (该情况在其他地方处理)，所以我们需要
	 * 如果它被 FOR UPDATE/SHARE 锁定，则使用 RowShareLock，否则
	 * 使用普通的 AccessShareLock。
	 */
	fc_lockmode = isLockedRefname(fc_pstate, fc_refname) ? RowShareLock : AccessShareLock;

	/*
	 * 获取关系的 OID。此访问还确保我们有一个最新的
	 * 关系缓存条目。由于这通常是对语句中某个关系的第一次访问，
	 * 我们必须以适当的 lockmode 打开该关系。
	 */
	fc_rel = parserOpenTable(fc_pstate, fc_relation, fc_lockmode);
	fc_rte->relid = RelationGetRelid(fc_rel);
	fc_rte->relkind = fc_rel->rd_rel->relkind;
	fc_rte->rellockmode = fc_lockmode;

	/*
	 * 使用用户提供的别名和/或实际列名构建有效列名列表。
	 */
	fc_rte->eref = makeAlias(fc_refname, NIL);
	fc_buildRelationAliases(fc_rel->rd_att, fc_alias, fc_rte->eref);

	/*
	 * 设置标志和访问权限。
	 *
	 * 在访问检查上的初始默认值始终是检查读取权限，
	 * 对所有目标表以外的情况都是正确的。
	 */
	fc_rte->lateral = false;
	fc_rte->inh = fc_inh;
	fc_rte->inFromCl = fc_inFromCl;

	fc_rte->requiredPerms = ACL_SELECT;
	fc_rte->checkAsUser = InvalidOid;	/* 默认情况下，也不设置为 set-uid */
	fc_rte->selectedCols = NULL;
	fc_rte->insertedCols = NULL;
	fc_rte->updatedCols = NULL;
	fc_rte->extraUpdatedCols = NULL;

	/*
	 * 将已完成的 RTE 添加到 pstate 的范围表列表中，以便我们知道其
	 * 索引。但我们不将其添加到连接列表中——如果合适，调用者必须这样做。
	 */
	fc_pstate->p_rtable = lappend(fc_pstate->p_rtable, fc_rte);

	/*
	 * 构建一个 ParseNamespaceItem，但不要将其添加到 pstate 的名称空间
	 * 列表中——如果合适，调用者必须这样做。
	 */
	fc_nsitem = fc_buildNSItemFromTupleDesc(fc_rte, list_length(fc_pstate->p_rtable),
									  fc_rel->rd_att);

	/*
	 * 减少关系的引用计数，但在事务结束之前保持访问锁，
	 * 以便该表不能被删除或在我们之下修改其模式。
	 */
	table_close(fc_rel, NoLock);

	return fc_nsitem;
}

/*
 * 为关系添加一个条目到 pstate 的范围表 (p_rtable)。
 * 然后，构造并返回一个新的 RTE 的 ParseNamespaceItem。
 *
 * 这与 addRangeTableEntry() 类似，只是它使一个已打开关系的 RTE
 * 而不是 RangeVar 引用。
 *
 * lockmode 是查询执行所需的锁类型；根据 RTE 在查询中的角色，
 * 它必须是 AccessShareLock、RowShareLock 或 RowExclusiveLock 之一。
 * 调用者必须保持该锁模式或更强的锁。
 *
 * 注意: 正确地说，lockmode 应该声明为 LOCKMODE 而不是 int，但这
 * 需要将 storage/lock.h 导入到 parse_relation.h 中。由于
 * LOCKMODE 仍然是 typedef 为 int，因此这似乎是多此一举。
 */
ParseNamespaceItem * addRangeTableEntryForRelation(ParseState *fc_pstate,
							  Relation fc_rel,
							  int fc_lockmode,
							  Alias *fc_alias,
							  bool fc_inh,
							  bool fc_inFromCl)
{
	RangeTblEntry *fc_rte = makeNode(RangeTblEntry);
	char	   *fc_refname = fc_alias ? fc_alias->aliasname : RelationGetRelationName(fc_rel);

	Assert(fc_pstate != NULL);

	Assert(fc_lockmode == AccessShareLock ||
		   fc_lockmode == RowShareLock ||
		   fc_lockmode == RowExclusiveLock);
	Assert(CheckRelationLockedByMe(fc_rel, fc_lockmode, true));

	fc_rte->rtekind = RTE_RELATION;
	fc_rte->alias = fc_alias;
	fc_rte->relid = RelationGetRelid(fc_rel);
	fc_rte->relkind = fc_rel->rd_rel->relkind;
	fc_rte->rellockmode = fc_lockmode;

	/*
	 * 使用用户提供的别名和/或实际列名构建有效列名列表。
	 */
	fc_rte->eref = makeAlias(fc_refname, NIL);
	fc_buildRelationAliases(fc_rel->rd_att, fc_alias, fc_rte->eref);

	/*
	 * 设置标志和访问权限。
	 *
	 * 在访问检查上的初始默认值始终是检查读取权限，
	 * 对所有目标表以外的情况都是正确的。
	 */
	fc_rte->lateral = false;
	fc_rte->inh = fc_inh;
	fc_rte->inFromCl = fc_inFromCl;

	fc_rte->requiredPerms = ACL_SELECT;
	fc_rte->checkAsUser = InvalidOid;	/* 默认情况下，也不设置为 set-uid */
	fc_rte->selectedCols = NULL;
	fc_rte->insertedCols = NULL;
	fc_rte->updatedCols = NULL;
	fc_rte->extraUpdatedCols = NULL;

	/*
	 * 将已完成的 RTE 添加到 pstate 的范围表列表中，以便我们知道其
	 * 索引。但我们不将其添加到连接列表中——如果合适，调用者必须这样做。
	 */
	fc_pstate->p_rtable = lappend(fc_pstate->p_rtable, fc_rte);

	/*
	 * 构建一个 ParseNamespaceItem，但不要将其添加到 pstate 的名称空间
	 * 列表中——如果合适，调用者必须这样做。
	 */
	return fc_buildNSItemFromTupleDesc(fc_rte, list_length(fc_pstate->p_rtable),
									fc_rel->rd_att);
}

/*
 * 为子查询添加一个条目到 pstate 的范围表 (p_rtable)。
 * 然后，构造并返回一个新的 RTE 的 ParseNamespaceItem。
 *
 * 这与 addRangeTableEntry() 类似，只是它使一个子查询 RTE。
 * 请注意，必须提供别名子句。
 */
ParseNamespaceItem * addRangeTableEntryForSubquery(ParseState *fc_pstate,
							  Query *fc_subquery,
							  Alias *fc_alias,
							  bool fc_lateral,
							  bool fc_inFromCl)
{
	RangeTblEntry *fc_rte = makeNode(RangeTblEntry);
	char	   *fc_refname = fc_alias->aliasname;
	Alias	   *fc_eref;
	int			fc_numaliases;
	List	   *fc_coltypes,
			   *fc_coltypmods,
			   *fc_colcollations;
	int			fc_varattno;
	ListCell   *fc_tlistitem;

	Assert(fc_pstate != NULL);

	fc_rte->rtekind = RTE_SUBQUERY;
	fc_rte->subquery = fc_subquery;
	fc_rte->alias = fc_alias;

	fc_eref = copyObject(fc_alias);
	fc_numaliases = list_length(fc_eref->colnames);

	/* 填充任何未指定的别名列，并提取列类型信息 */
	fc_coltypes = fc_coltypmods = fc_colcollations = NIL;
	fc_varattno = 0;
	foreach(fc_tlistitem, fc_subquery->targetList)
	{
		TargetEntry *fc_te = (TargetEntry *) lfirst(fc_tlistitem);

		if (fc_te->resjunk)
			continue;
		fc_varattno++;
		Assert(fc_varattno == fc_te->resno);
		if (fc_varattno > fc_numaliases)
		{
			char	   *fc_attrname;

			fc_attrname = pstrdup(fc_te->resname);
			fc_eref->colnames = lappend(fc_eref->colnames, makeString(fc_attrname));
		}
		fc_coltypes = lappend_oid(fc_coltypes,
							   exprType((Node *) fc_te->expr));
		fc_coltypmods = lappend_int(fc_coltypmods,
								 exprTypmod((Node *) fc_te->expr));
		fc_colcollations = lappend_oid(fc_colcollations,
									exprCollation((Node *) fc_te->expr));
	}
	if (fc_varattno < fc_numaliases)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
				 errmsg("table \"%s\" has %d columns available but %d columns specified",
						fc_refname, fc_varattno, fc_numaliases)));

	fc_rte->eref = fc_eref;

	/*
	 * 设置标志和访问权限。
	 *
	 * 子查询从不检查访问权限。
	 */
	fc_rte->lateral = fc_lateral;
	fc_rte->inh = false;			/* 对于子查询而言从不为真 */
	fc_rte->inFromCl = fc_inFromCl;

	fc_rte->requiredPerms = 0;
	fc_rte->checkAsUser = InvalidOid;
	fc_rte->selectedCols = NULL;
	fc_rte->insertedCols = NULL;
	fc_rte->updatedCols = NULL;
	fc_rte->extraUpdatedCols = NULL;

	/*
	 * 将已完成的 RTE 添加到 pstate 的范围表列表中，以便我们知道其
	 * 索引。但我们不将其添加到连接列表中——如果合适，调用者必须这样做。
	 */
	fc_pstate->p_rtable = lappend(fc_pstate->p_rtable, fc_rte);

	/*
	 * 构建一个 ParseNamespaceItem，但不要将其添加到 pstate 的名称空间
	 * 列表中——如果合适，调用者必须这样做。
	 */
	return fc_buildNSItemFromLists(fc_rte, list_length(fc_pstate->p_rtable),
								fc_coltypes, fc_coltypmods, fc_colcollations);
}

/*
 * 为函数（或函数）添加一个条目到 pstate 的范围表
 * (p_rtable)。然后，构造并返回一个新的 RTE 的 ParseNamespaceItem。
 *
 * 这与 addRangeTableEntry() 类似，只是它使一个函数 RTE。
 */
ParseNamespaceItem * addRangeTableEntryForFunction(ParseState *fc_pstate,
							  List *fc_funcnames,
							  List *fc_funcexprs,
							  List *fc_coldeflists,
							  RangeFunction *fc_rangefunc,
							  bool fc_lateral,
							  bool fc_inFromCl)
{
	RangeTblEntry *fc_rte = makeNode(RangeTblEntry);
	Alias	   *fc_alias = fc_rangefunc->alias;
	Alias	   *fc_eref;
	char	   *fc_aliasname;
	int			fc_nfuncs = list_length(fc_funcexprs);
	TupleDesc  *fc_functupdescs;
	TupleDesc	fc_tupdesc;
	ListCell   *fc_lc1,
			   *fc_lc2,
			   *fc_lc3;
	int			fc_i;
	int			fc_j;
	int			fc_funcno;
	int			fc_natts,
				fc_totalatts;

	Assert(fc_pstate != NULL);

	fc_rte->rtekind = RTE_FUNCTION;
	fc_rte->relid = InvalidOid;
	fc_rte->subquery = NULL;
	fc_rte->functions = NIL;		

/* 我们将在下面填充这个列表 */
	fc_rte->funcordinality = fc_rangefunc->ordinality;
	fc_rte->alias = fc_alias;

	/*
	 * 选择 RTE 别名名称。即使有多个函数，我们也默认使用第一个函数的
	 * 名称；这可能有争议，但比使用像“table”这样的常量要好。
	 */
	if (fc_alias)
		fc_aliasname = fc_alias->aliasname;
	else
		fc_aliasname = linitial(fc_funcnames);

	fc_eref = makeAlias(fc_aliasname, NIL);
	fc_rte->eref = fc_eref;

	/* 处理每个函数... */
	fc_functupdescs = (TupleDesc *) palloc(fc_nfuncs * sizeof(TupleDesc));

	fc_totalatts = 0;
	fc_funcno = 0;
	forthree(fc_lc1, fc_funcexprs, fc_lc2, fc_funcnames, fc_lc3, fc_coldeflists)
	{
		Node	   *fc_funcexpr = (Node *) lfirst(fc_lc1);
		char	   *fc_funcname = (char *) lfirst(fc_lc2);
		List	   *fc_coldeflist = (List *) lfirst(fc_lc3);
		RangeTblFunction *fc_rtfunc = makeNode(RangeTblFunction);
		TypeFuncClass fc_functypclass;
		Oid			fc_funcrettype;

		/* 初始化 RangeTblFunction 节点 */
		fc_rtfunc->funcexpr = fc_funcexpr;
		fc_rtfunc->funccolnames = NIL;
		fc_rtfunc->funccoltypes = NIL;
		fc_rtfunc->funccoltypmods = NIL;
		fc_rtfunc->funccolcollations = NIL;
		fc_rtfunc->funcparams = NULL;	/* 计划前未设置 */

		/*
		 * 现在确定函数返回的是简单类型还是复合类型。
		 */
		fc_functypclass = get_expr_result_type(fc_funcexpr,
											&fc_funcrettype,
											&fc_tupdesc);

		/*
		 * 如果函数返回 RECORD 且没有预定的记录类型，则需要 coldeflist，
		 * 否则是禁止的。这可能有点混淆，因此我们花费一些精力来提供
		 * 相关的错误信息。
		 */
		if (fc_coldeflist != NIL)
		{
			switch (fc_functypclass)
			{
				case TYPEFUNC_RECORD:
					/* ok */
					break;
				case TYPEFUNC_COMPOSITE:
				case TYPEFUNC_COMPOSITE_DOMAIN:

					/*
					 * 如果函数的原始结果类型为 RECORD，我们必须
					 * 使用其 OUT 参数解析它。否则，
					 * 它必须有一个命名的复合类型。
					 */
					if (exprType(fc_funcexpr) == RECORDOID)
						ereport(ERROR,
								(errcode(ERRCODE_SYNTAX_ERROR),
								 errmsg("a column definition list is redundant for a function with OUT parameters"),
								 parser_errposition(fc_pstate,
													exprLocation((Node *) fc_coldeflist))));
					else
						ereport(ERROR,
								(errcode(ERRCODE_SYNTAX_ERROR),
								 errmsg("a column definition list is redundant for a function returning a named composite type"),
								 parser_errposition(fc_pstate,
													exprLocation((Node *) fc_coldeflist))));
					break;
				default:
					ereport(ERROR,
							(errcode(ERRCODE_SYNTAX_ERROR),
							 errmsg("a column definition list is only allowed for functions returning \"record\""),
							 parser_errposition(fc_pstate,
												exprLocation((Node *) fc_coldeflist))));
					break;
			}
		}
		else
		{
			if (fc_functypclass == TYPEFUNC_RECORD)
				ereport(ERROR,
						(errcode(ERRCODE_SYNTAX_ERROR),
						 errmsg("a column definition list is required for functions returning \"record\""),
						 parser_errposition(fc_pstate, exprLocation(fc_funcexpr))));
		}

		if (fc_functypclass == TYPEFUNC_COMPOSITE ||
			fc_functypclass == TYPEFUNC_COMPOSITE_DOMAIN)
		{
			/* 复合数据类型，例如表的行类型 */
			Assert(fc_tupdesc);
		}
		else if (fc_functypclass == TYPEFUNC_SCALAR)
		{
			/* 基本数据类型，即标量 */
			fc_tupdesc = CreateTemplateTupleDesc(1);
			TupleDescInitEntry(fc_tupdesc,
							   (AttrNumber) 1,
							   fc_chooseScalarFunctionAlias(fc_funcexpr, fc_funcname,
														 fc_alias, fc_nfuncs),
							   fc_funcrettype,
							   exprTypmod(fc_funcexpr),
							   0);
			TupleDescInitEntryCollation(fc_tupdesc,
										(AttrNumber) 1,
										exprCollation(fc_funcexpr));
		}
		else if (fc_functypclass == TYPEFUNC_RECORD)
		{
			ListCell   *fc_col;

			/*
			 * 使用列定义列表构造 tupdesc 并填充
			 * RangeTblFunction 的列表。将列数限制为
			 * MaxHeapAttributeNumber，因为 CheckAttributeNamesTypes 会这样做。
			 */
			if (list_length(fc_coldeflist) > MaxHeapAttributeNumber)
				ereport(ERROR,
						(errcode(ERRCODE_TOO_MANY_COLUMNS),
						 errmsg("column definition lists can have at most %d entries",
								MaxHeapAttributeNumber),
						 parser_errposition(fc_pstate,
											exprLocation((Node *) fc_coldeflist))));
			fc_tupdesc = CreateTemplateTupleDesc(list_length(fc_coldeflist));
			fc_i = 1;
			foreach(fc_col, fc_coldeflist)
			{
				ColumnDef  *fc_n = (ColumnDef *) lfirst(fc_col);
				char	   *fc_attrname;
				Oid			fc_attrtype;
				int32		fc_attrtypmod;
				Oid			fc_attrcollation;

				fc_attrname = fc_n->colname;
				if (fc_n->typeName->setof)
					ereport(ERROR,
							(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
							 errmsg("column \"%s\" cannot be declared SETOF",
									fc_attrname),
							 parser_errposition(fc_pstate, fc_n->location)));
				typenameTypeIdAndMod(fc_pstate, fc_n->typeName,
									 &fc_attrtype, &fc_attrtypmod);
				fc_attrcollation = GetColumnDefCollation(fc_pstate, fc_n, fc_attrtype);
				TupleDescInitEntry(fc_tupdesc,
								   (AttrNumber) fc_i,
								   fc_attrname,
								   fc_attrtype,
								   fc_attrtypmod,
								   0);
				TupleDescInitEntryCollation(fc_tupdesc,
											(AttrNumber) fc_i,
											fc_attrcollation);
				fc_rtfunc->funccolnames = lappend(fc_rtfunc->funccolnames,
											   makeString(pstrdup(fc_attrname)));
				fc_rtfunc->funccoltypes = lappend_oid(fc_rtfunc->funccoltypes,
												   fc_attrtype);
				fc_rtfunc->funccoltypmods = lappend_int(fc_rtfunc->funccoltypmods,
													 fc_attrtypmod);
				fc_rtfunc->funccolcollations = lappend_oid(fc_rtfunc->funccolcollations,
														fc_attrcollation);

				fc_i++;
			}

			/*
			 * 确保 coldeflist 定义了一组合法的名称（没有
			 * 重复，但我们不需要担心系统列名称）和
			 * 数据类型。虽然我们大多数情况下不能允许伪类型，但
			 * 似乎允许 RECORD 和 RECORD[] 是安全的，因为
			 * 这些类型类中的值在运行时是自我识别的，并且
			 * coldeflist 不代表其他会话可见的任何内容。
			 */
			CheckAttributeNamesTypes(fc_tupdesc, RELKIND_COMPOSITE_TYPE,
									 CHKATYPE_ANYRECORD);
		}
		else
			ereport(ERROR,
					(errcode(ERRCODE_DATATYPE_MISMATCH),
					 errmsg("function \"%s\" in FROM has unsupported return type %s",
							fc_funcname, format_type_be(fc_funcrettype)),
					 parser_errposition(fc_pstate, exprLocation(fc_funcexpr))));

		/* 完成 RangeTblFunction，并将其添加到 RTE 的列表中 */
		fc_rtfunc->funccolcount = fc_tupdesc->natts;
		fc_rte->functions = lappend(fc_rte->functions, fc_rtfunc);

		/* 保存 tupdesc 以供后用 */
		fc_functupdescs[fc_funcno] = fc_tupdesc;
		fc_totalatts += fc_tupdesc->natts;
		fc_funcno++;
	}

	/*
	 * 如果有多个函数，或者我们想要一个序列号列，
	 * 我们必须生成一个合并的 tupdesc。
	 */
	if (fc_nfuncs > 1 || fc_rangefunc->ordinality)
	{
		if (fc_rangefunc->ordinality)
			fc_totalatts++;

		/* 不允许列数超过元组可容纳的数量 */
		if (fc_totalatts > MaxTupleAttributeNumber)
			ereport(ERROR,
					(errcode(ERRCODE_TOO_MANY_COLUMNS),
					 errmsg("functions in FROM can return at most %d columns",
							MaxTupleAttributeNumber),
					 parser_errposition(fc_pstate,
										exprLocation((Node *) fc_funcexprs))));

		/* 将每个函数的元组描述合并为一个复合描述 */
		fc_tupdesc = CreateTemplateTupleDesc(fc_totalatts);
		fc_natts = 0;
		for (fc_i = 0; fc_i < fc_nfuncs; fc_i++)
		{
			for (fc_j = 1; fc_j <= fc_functupdescs[fc_i]->natts; fc_j++)
				TupleDescCopyEntry(fc_tupdesc, ++fc_natts, fc_functupdescs[fc_i], fc_j);
		}

		/* 如果需要，添加序列号列 */
		if (fc_rangefunc->ordinality)
		{
			TupleDescInitEntry(fc_tupdesc,
							   (AttrNumber) ++fc_natts,
							   "ordinality",
							   INT8OID,
							   -1,
							   0);
			/* 无需设置排序规则 */
		}

		Assert(fc_natts == fc_totalatts);
	}
	else
	{
		/* 我们可以直接使用单个函数的 tupdesc */
		fc_tupdesc = fc_functupdescs[0];
	}

	/* 在为 RTE 分配列别名时使用 tupdesc */
	fc_buildRelationAliases(fc_tupdesc, fc_alias, fc_eref);

	/*
	 * 设置标志和访问权限。
	 *
	 * 函数永远不会被检查访问权限（至少，不是通过 RTE
	 * 权限机制）。
	 */
	fc_rte->lateral = fc_lateral;
	fc_rte->inh = false;			/* 对于函数来说永远不成立 */
	fc_rte->inFromCl = fc_inFromCl;

	fc_rte->requiredPerms = 0;
	fc_rte->checkAsUser = InvalidOid;
	fc_rte->selectedCols = NULL;
	fc_rte->insertedCols = NULL;
	fc_rte->updatedCols = NULL;
	fc_rte->extraUpdatedCols = NULL;

	/*
	 * 将已完成的 RTE 添加到 pstate 的范围表列表中，以便我们知道其
	 * 索引。但我们不将其添加到连接列表中——如果合适，调用者必须这样做。
	 */
	fc_pstate->p_rtable = lappend(fc_pstate->p_rtable, fc_rte);

	/*
	 * 构建一个 ParseNamespaceItem，但不要将其添加到 pstate 的名称空间
	 * 列表中——如果合适，调用者必须这样做。
	 */
	return fc_buildNSItemFromTupleDesc(fc_rte, list_length(fc_pstate->p_rtable),
									fc_tupdesc);
}

/*
 * 向 pstate 的范围表 (p_rtable) 添加表函数的条目。
 * 然后，构造并返回新的 RTE 的 ParseNamespaceItem。
 *
 * 这与 addRangeTableEntry() 很相似，除了它创建一个 tablefunc RTE。
 */
ParseNamespaceItem * addRangeTableEntryForTableFunc(ParseState *fc_pstate,
							   TableFunc *fc_tf,
							   Alias *fc_alias,
							   bool fc_lateral,
							   bool fc_inFromCl)
{
	RangeTblEntry *fc_rte = makeNode(RangeTblEntry);
	char	   *fc_refname;
	Alias	   *fc_eref;
	int			fc_numaliases;

	Assert(fc_pstate != NULL);

	/* 不允许列数超过元组可容纳的数量 */
	if (list_length(fc_tf->colnames) > MaxTupleAttributeNumber)
		ereport(ERROR,
				(errcode(ERRCODE_TOO_MANY_COLUMNS),
				 errmsg("functions in FROM can return at most %d columns",
						MaxTupleAttributeNumber),
				 parser_errposition(fc_pstate,
									exprLocation((Node *) fc_tf))));
	Assert(list_length(fc_tf->coltypes) == list_length(fc_tf->colnames));
	Assert(list_length(fc_tf->coltypmods) == list_length(fc_tf->colnames));
	Assert(list_length(fc_tf->colcollations) == list_length(fc_tf->colnames));

	fc_refname = fc_alias ? fc_alias->aliasname : pstrdup("xmltable");

	fc_rte->rtekind = RTE_TABLEFUNC;
	fc_rte->relid = InvalidOid;
	fc_rte->subquery = NULL;
	fc_rte->tablefunc = fc_tf;
	fc_rte->coltypes = fc_tf->coltypes;
	fc_rte->coltypmods = fc_tf->coltypmods;
	fc_rte->colcollations = fc_tf->colcollations;
	fc_rte->alias = fc_alias;

	fc_eref = fc_alias ? copyObject(fc_alias) : makeAlias(fc_refname, NIL);
	fc_numaliases = list_length(fc_eref->colnames);

	/* 填充任何未指定的别名列 */
	if (fc_numaliases < list_length(fc_tf->colnames))
		fc_eref->colnames = list_concat(fc_eref->colnames,
									 list_copy_tail(fc_tf->colnames, fc_numaliases));

	if (fc_numaliases > list_length(fc_tf->colnames))
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
				 errmsg("%s function has %d columns available but %d columns specified",
						"XMLTABLE",
						list_length(fc_tf->colnames), fc_numaliases)));

	fc_rte->eref = fc_eref;

	/*
	 * 设置标志和访问权限。
	 *
	 * 表函数永远不会被检查访问权限（至少，不是通过 RTE
	 * 权限机制）。
	 */
	fc_rte->lateral = fc_lateral;
	fc_rte->inh = false;			/* 对于 tablefunc RTEs 永远不成立 */
	fc_rte->inFromCl = fc_inFromCl;

	fc_rte->requiredPerms = 0;
	fc_rte->checkAsUser = InvalidOid;
	fc_rte->selectedCols = NULL;
	fc_rte->insertedCols = NULL;
	fc_rte->updatedCols = NULL;
	fc_rte->extraUpdatedCols = NULL;

	/*
	 * 将已完成的 RTE 添加到 pstate 的范围表列表中，以便我们知道其
	 * 索引。但我们不将其添加到连接列表中——如果合适，调用者必须这样做。
	 */
	fc_pstate->p_rtable = lappend(fc_pstate->p_rtable, fc_rte);

	/*
	 * 构建一个 ParseNamespaceItem，但不要将其添加到 pstate 的名称空间
	 * 列表中——如果合适，调用者必须这样做。
	 */
	return fc_buildNSItemFromLists(fc_rte, list_length(fc_pstate->p_rtable),
								fc_rte->coltypes, fc_rte->coltypmods,
								fc_rte->colcollations);
}

/*
 * 向 pstate 的范围表 (p_rtable) 添加 VALUES 列表的条目。
 * 然后，构造并返回新的 RTE 的 ParseNamespaceItem。
 *
 * 这与 addRangeTableEntry() 很相似，除了它创建一个 values RTE。
 */
ParseNamespaceItem * addRangeTableEntryForValues(ParseState *fc_pstate,
							List *fc_exprs,
							List *fc_coltypes,
							List *fc_coltypmods,
							List *fc_colcollations,
							Alias *fc_alias,
							bool fc_lateral,
							bool fc_inFromCl)
{
	RangeTblEntry *fc_rte = makeNode(RangeTblEntry);
	char	   *fc_refname = fc_alias ? fc_alias->aliasname : pstrdup("*VALUES*");
	Alias	   *fc_eref;
	int			fc_numaliases;
	int			fc_numcolumns;

	Assert(fc_pstate != NULL);

	fc_rte->rtekind = RTE_VALUES;
	fc_rte->relid = InvalidOid;
	fc_rte->subquery = NULL;
	fc_rte->values_lists = fc_exprs;
	fc_rte->coltypes = fc_coltypes;
	fc_rte->coltypmods = fc_coltypmods;
	fc_rte->colcollations = fc_colcollations;
	fc_rte->alias = fc_alias;

	fc_eref = fc_alias ? copyObject(fc_alias) : makeAlias(fc_refname, NIL);

	/* 填充任何未指定的别名列 */
	fc_numcolumns = list_length((List *) linitial(fc_exprs));
	fc_numaliases = list_length(fc_eref->colnames);
	while (fc_numaliases < fc_numcolumns)
	{
		char		fc_attrname[64];

		fc_numaliases++;
		snprintf(fc_attrname, sizeof(fc_attrname), "column%d", fc_numaliases);
		fc_eref->colnames = lappend(fc_eref->colnames,
								 makeString(pstrdup(fc_attrname)));
	}
	if (fc_numcolumns < fc_numaliases)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
				 errmsg("VALUES lists \"%s\" have %d columns available but %d columns specified",
						fc_refname, fc_numcolumns, fc_numaliases)));

	fc_rte->eref = fc_eref;

	/*
	 * 设置标志和访问权限。
	 *
	 * 子查询从不检查访问权限。
	 */
	fc_rte->lateral = fc_lateral;
	fc_rte->inh = false;			/* 对于 values RTEs 永远不成立 */
	fc_rte->inFromCl = fc_inFromCl;

	fc_rte->requiredPerms = 0;
	fc_rte->checkAsUser = InvalidOid;
	fc_rte->selectedCols = NULL;
	fc_rte->insertedCols = NULL;
	fc_rte->updatedCols = NULL;
	fc_rte->extraUpdatedCols = NULL;

	/*
	 * 将已完成的 RTE 添加到 pstate 的范围表列表中，以便我们知道其
	 * 索引。但我们不将其添加到连接列表中——如果合适，调用者必须这样做。
	 */
	fc_pstate->p_rtable = lappend(fc_pstate->p_rtable, fc_rte);

	/*
	 * 构建一个 ParseNamespaceItem，但不要将其添加到 pstate 的名称空间
	 * 列表中——如果合适，调用者必须这样做。
	 */
	return fc_buildNSItemFromLists(fc_rte, list_length(fc_pstate->p_rtable),
								fc_rte->coltypes, fc_rte->coltypmods,
								fc_rte->colcollations);
}

/*
 * 向 pstate 的范围表 (p_rtable) 添加连接的条目。
 * 然后，构造并返回新的 RTE 的 ParseNamespaceItem。
 *
 * 这与 addRangeTableEntry() 很相似，除了它创建一个 join RTE。
 * 此外，调用者构造 ParseNamespaceColumn 数组更方便，
 * 所以我们将其传入。
 */
ParseNamespaceItem * addRangeTableEntryForJoin(ParseState *fc_pstate,
						  List *fc_colnames,
						  ParseNamespaceColumn *fc_nscolumns,
						  JoinType fc_jointype,
						  int fc_nummergedcols,
						  List *fc_aliasvars,
						  List *fc_leftcols,
						  List *fc_rightcols,
						  Alias *fc_join_using_alias,
						  Alias *fc_alias,
						  bool fc_inFromCl)
{
	RangeTblEntry *fc_rte = makeNode(RangeTblEntry);
	Alias	   *fc_eref;
	int			fc_numaliases;
	ParseNamespaceItem *fc_nsitem;

	Assert(fc_pstate != NULL);

	/*
	 * 如果连接的列太多则失败——我们必须能够使用 AttrNumber 引用任何
	 * 列。
	 */
	if (list_length(fc_aliasvars) > MaxAttrNumber)
		ereport(ERROR,
				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
				 errmsg("joins can have at most %d columns",
						MaxAttrNumber)));

	fc_rte->rtekind = RTE_JOIN;
	fc_rte->relid = InvalidOid;
	fc_rte->subquery = NULL;
	fc_rte->jointype = fc_jointype;
	fc_rte->joinmergedcols = fc_nummergedcols;
	fc_rte->joinaliasvars = fc_aliasvars;
	fc_rte->joinleftcols = fc_leftcols;
	fc_rte->joinrightcols = fc_rightcols;
	fc_rte->join_using_alias = fc_join_using_alias;
	fc_rte->alias = fc_alias;

	fc_eref = fc_alias ? copyObject(fc_alias) : makeAlias("unnamed_join", NIL);
	fc_numaliases = list_length(fc_eref->colnames);

	/* 填充任何未指定的别名列 */
	if (fc_numaliases < list_length(fc_colnames))
		fc_eref->colnames = list_concat(fc_eref->colnames,
									 list_copy_tail(fc_colnames, fc_numaliases));

	if (fc_numaliases > list_length(fc_colnames))
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
				 errmsg("join expression \"%s\" has %d columns available but %d columns specified",
						fc_eref->aliasname, list_length(fc_colnames), fc_numaliases)));

	fc_rte->eref = fc_eref;

	/*
	 * 设置标志和访问权限。
	 *
	 * 连接永远不会被检查访问权限。
	 */
	fc_rte->lateral = false;
	fc_rte->inh = false;			/* 对于连接来说永远不成立 */
	fc_rte->inFromCl = fc_inFromCl;

	fc_rte->requiredPerms = 0;
	fc_rte->checkAsUser = InvalidOid;
	fc_rte->selectedCols = NULL;
	fc_rte->insertedCols = NULL;
	fc_rte->updatedCols = NULL;
	fc_rte->extraUpdatedCols = NULL;

	/*
	 * 将已完成的 RTE 添加到 pstate 的范围表列表中，以便我们知道其
	 * 索引。但我们不将其添加到连接列表中——如果合适，调用者必须这样做。
	 */
	fc_pstate->p_rtable = lappend(fc_pstate->p_rtable, fc_rte);

	/*
	 * 构建一个 ParseNamespaceItem，但不要将其添加到 pstate 的名称空间
	 * 列表中——如果合适，调用者必须这样做。
	 */
	fc_nsitem = (ParseNamespaceItem *) palloc(sizeof(ParseNamespaceItem));
	fc_nsitem->p_names = fc_rte->eref;
	fc_nsitem->p_rte = fc_rte;
	fc_nsitem->p_rtindex = list_length(fc_pstate->p_rtable);
	fc_nsitem->p_nscolumns = fc_nscolumns;
	/* 设置默认可见性标志；可能稍后会更改 */
	fc_nsitem->p_rel_visible = true;
	fc_nsitem->p_cols_visible = true;
	fc_nsitem->p_lateral_only = false;
	fc_nsitem->p_lateral_ok = true;

	return fc_nsitem;
}

/*
 * 向 pstate 的范围表 (p_rtable) 添加 CTE 引用的条目。
 * 然后，构造并返回新的 RTE 的 ParseNamespaceItem。
 *
 * 这与 addRangeTableEntry() 很相似，除了它创建一个 CTE RTE。
 */
ParseNamespaceItem * addRangeTableEntryForCTE(ParseState *fc_pstate,
						 CommonTableExpr *fc_cte,
						 Index fc_levelsup,
						 RangeVar *fc_rv,
						 bool fc_inFromCl)
{
	RangeTblEntry *fc_rte = makeNode(RangeTblEntry);
	Alias	   *fc_alias = fc_rv->alias;
	char	   *fc_refname = fc_alias ? fc_alias->aliasname : fc_cte->ctename;
	Alias	   *fc_eref;
	int			fc_numaliases;
	int			fc_varattno;
	ListCell   *fc_lc;
	int			fc_n_dontexpand_columns = 0;
	ParseNamespaceItem *fc_psi;

	Assert(fc_pstate != NULL);

	fc_rte->rtekind = RTE_CTE;
	fc_rte->ctename = fc_cte->ctename;
	fc_rte->ctelevelsup = fc_levelsup;

	/* 仅当 CTE 的解析分析未完成时自引用 */
	fc_rte->self_reference = !IsA(fc_cte->ctequery, Query);
	Assert(fc_cte->cterecursive || !fc_rte->self_reference);
	/* 如果这不是自引用，则增加 CTE 的引用计数 */
	if (!fc_rte->self_reference)
		fc_cte->cterefcount++;

	/*
	 * 如果 CTE 是 INSERT/UPDATE/DELETE 而没有 RETURNING，我们将抛出错误。
	 * 在自引用的情况下不会检查这一点，但没关系，因为
	 * 数据修改 CTE 不允许是递归的。
	 */
	if (IsA(fc_cte->ctequery, Query))
	{
		Query	   *fc_ctequery = (Query *) fc_cte->ctequery;

		if (fc_ctequery->commandType != CMD_SELECT &&
			fc_ctequery->returningList == NIL)
			ereport(ERROR,
					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
					 errmsg("WITH query \"%s\" does not have a RETURNING clause",
							fc_cte->ctename),
					 parser_errposition(fc_pstate, fc_rv->location)));
	}

	fc_rte->coltypes = list_copy(fc_cte->ctecoltypes);
	fc_rte->coltypmods = list_copy(fc_cte->ctecoltypmods);
	fc_rte->colcollations = list_copy(fc_cte->ctecolcollations);

	fc_rte->alias = fc_alias;
	if (fc_alias)
		fc_eref = copyObject(fc_alias);
	else
		fc_eref = makeAlias(fc_refname, NIL);
	fc_numaliases = list_length(fc_eref->colnames);

	/* 填充任何未指定的别名列 */
	fc_varattno = 0;
	foreach(fc_lc, fc_cte->ctecolnames)
	{
		fc_varattno++;
		if (fc_varattno > fc_numaliases)
			fc_eref->colnames = lappend(fc_eref->colnames, lfirst(fc_lc));
	}
	if (fc_varattno < fc_numaliases)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
				 errmsg("table \"%s\" has %d columns available but %d columns specified",
						fc_refname, fc_varattno, fc_numaliases)));

	fc_rte->eref = fc_eref;

	if (fc_cte->search_clause)
	{
		fc_rte->eref->colnames = lappend(fc_rte->eref->colnames, makeString(fc_cte->search_clause->search_seq_column));
		if (fc_cte->search_clause->search_breadth_first)
			fc_rte->coltypes = lappend_oid(fc_rte->coltypes, RECORDOID);
		else
			fc_rte->coltypes = lappend_oid(fc_rte->coltypes, RECORDARRAYOID);
		fc_rte->coltypmods = lappend_int(fc_rte->coltypmods, -1);
		fc_rte->colcollations = lappend_oid(fc_rte->colcollations, InvalidOid);

		fc_n_dontexpand_columns += 1;
	}

	if (fc_cte->cycle_clause)
	{
		fc_rte->eref->colnames = lappend(fc_rte->eref->colnames, makeString(fc_cte->cycle_clause->cycle_mark_column));
		fc_rte->coltypes = lappend_oid(fc_rte->coltypes, fc_cte->cycle_clause->cycle_mark_type);
		fc_rte->coltypmods = lappend_int(fc_rte->coltypmods, fc_cte->cycle_clause->cycle_mark_typmod);
		fc_rte->colcollations = lappend_oid(fc_rte->colcollations, fc_cte->cycle_clause->cycle_mark_collation);

		fc_rte->eref->colnames = lappend(fc_rte->eref->colnames, makeString(fc_cte->cycle_clause->cycle_path_column));
		fc_rte->coltypes = lappend_oid(fc_rte->coltypes, RECORDARRAYOID);
		fc_rte->coltypmods = lappend_int(fc_rte->coltypmods, -1);
		fc_rte->colcollations = lappend_oid(fc_rte->colcollations, InvalidOid);

		fc_n_dontexpand_columns += 2;
	}

	/*
	 * 设置标志和访问权限。
	 *
	 * 子查询从不检查访问权限。
	 */
	fc_rte->lateral = false;
	fc_rte->inh = false;			/* 对于子查询而言从不为真 */
	fc_rte->inFromCl = fc_inFromCl;

	fc_rte->requiredPerms = 0;
	fc_rte->checkAsUser = InvalidOid;
	fc_rte->selectedCols = NULL;
	fc_rte->insertedCols = NULL;
	fc_rte->updatedCols = NULL;
	fc_rte->extraUpdatedCols = NULL;

	/*
	 * 将已完成的 RTE 添加到 pstate 的范围表列表中，以便我们知道其
	 * 索引。但我们不将其添加到连接列表中——如果合适，调用者必须这样做。
	 */
	fc_pstate->p_rtable = lappend(fc_pstate->p_rtable, fc_rte);

	/*
	 * 构建一个 ParseNamespaceItem，但不要将其添加到 pstate 的名称空间
	 * 列表中——如果合适，调用者必须这样做。
	 */
	fc_psi = fc_buildNSItemFromLists(fc_rte, list_length(fc_pstate->p_rtable),
							   fc_rte->coltypes, fc_rte->coltypmods,
							   fc_rte->colcollations);

	/*
	 * 通过搜索和循环子句添加的列不包含在 CTE 中的
	 * 查询的星号扩展中。
	 */
	if (fc_rte->ctelevelsup > 0)
		for (int fc_i = 0; fc_i < fc_n_dontexpand_columns; fc_i++)
			fc_psi->p_nscolumns[list_length(fc_psi->p_names->colnames) - 1 - fc_i].p_dontexpand = true;

	return fc_psi;
}

/*
 * 向 pstate 的范围表 (p_rtable) 添加一个临时命名关系引用的条目。
 * 然后，构造并返回新的 RTE 的 ParseNamespaceItem。
 *
 * 预计 RangeVar，直到现在仅被认为是一个临时命名关系，
 * 将（结合 ParseState 中的 QueryEnvironment）创建一个具体*类别*
 * 的临时命名关系的 RangeTblEntry，基于 enrtype。
 *
 * 这与 addRangeTableEntry() 很相似，除了它为临时命名关系创建 RTE。
 */
ParseNamespaceItem * addRangeTableEntryForENR(ParseState *fc_pstate,
						 RangeVar *fc_rv,
						 bool fc_inFromCl)
{
	RangeTblEntry *fc_rte = makeNode(RangeTblEntry);
	Alias	   *fc_alias = fc_rv->alias;
	char	   *fc_refname = fc_alias ? fc_alias->aliasname : fc_rv->relname;
	EphemeralNamedRelationMetadata fc_enrmd;
	TupleDesc	fc_tupdesc;
	int			fc_attno;

	Assert(fc_pstate != NULL);
	fc_enrmd = get_visible_ENR(fc_pstate, fc_rv->relname);
	Assert(fc_enrmd != NULL);

	switch (fc_enrmd->enrtype)
	{
		case ENR_NAMED_TUPLESTORE:
			fc_rte->rtekind = RTE_NAMEDTUPLESTORE;
			break;

		default:
			elog(ERROR, "unexpected enrtype: %d", fc_enrmd->enrtype);
			return NULL;		/* 为了防止挑剔的编译器 */
	}

	/* 
	 * 记录对关系的依赖。这允许在访问与被更改的表关联的过渡表时使计划失效。
	 */
	fc_rte->relid = fc_enrmd->reliddesc;

	/*
	 * 使用用户提供的别名和/或实际列名构建有效列名列表。
	 */
	fc_tupdesc = ENRMetadataGetTupDesc(fc_enrmd);
	fc_rte->eref = makeAlias(fc_refname, NIL);
	fc_buildRelationAliases(fc_tupdesc, fc_alias, fc_rte->eref);

	/* 记录 ENR 的其他数据，包括列类型信息 */
	fc_rte->enrname = fc_enrmd->name;
	fc_rte->enrtuples = fc_enrmd->enrtuples;
	fc_rte->coltypes = NIL;
	fc_rte->coltypmods = NIL;
	fc_rte->colcollations = NIL;
	for (fc_attno = 1; fc_attno <= fc_tupdesc->natts; ++fc_attno)
	{
		Form_pg_attribute fc_att = TupleDescAttr(fc_tupdesc, fc_attno - 1);

		if (fc_att->attisdropped)
		{
			/* 记录被删除列的零值 */
			fc_rte->coltypes = lappend_oid(fc_rte->coltypes, InvalidOid);
			fc_rte->coltypmods = lappend_int(fc_rte->coltypmods, 0);
			fc_rte->colcollations = lappend_oid(fc_rte->colcollations, InvalidOid);
		}
		else
		{
			/* 确保我们可以确定这个没有被删除 */
			if (fc_att->atttypid == InvalidOid)
				elog(ERROR, "atttypid is invalid for non-dropped column in \"%s\"",
					 fc_rv->relname);
			fc_rte->coltypes = lappend_oid(fc_rte->coltypes, fc_att->atttypid);
			fc_rte->coltypmods = lappend_int(fc_rte->coltypmods, fc_att->atttypmod);
			fc_rte->colcollations = lappend_oid(fc_rte->colcollations,
											 fc_att->attcollation);
		}
	}

	/* 
	 * 设置标志和访问权限。
	 *
	 * ENR 从不检查访问权限。
	 */
	fc_rte->lateral = false;
	fc_rte->inh = false;			/* 对于 ENR 来说永远不为真 */
	fc_rte->inFromCl = fc_inFromCl;

	fc_rte->requiredPerms = 0;
	fc_rte->checkAsUser = InvalidOid;
	fc_rte->selectedCols = NULL;

	/*
	 * 将已完成的 RTE 添加到 pstate 的范围表列表中，以便我们知道其
	 * 索引。但我们不将其添加到连接列表中——如果合适，调用者必须这样做。
	 */
	fc_pstate->p_rtable = lappend(fc_pstate->p_rtable, fc_rte);

	/*
	 * 构建一个 ParseNamespaceItem，但不要将其添加到 pstate 的名称空间
	 * 列表中——如果合适，调用者必须这样做。
	 */
	return fc_buildNSItemFromTupleDesc(fc_rte, list_length(fc_pstate->p_rtable),
									fc_tupdesc);
}


/* 
 * 指定的 refname 是否已被选为 FOR UPDATE/FOR SHARE？
 *
 * 当我们还没有执行 transformLockingClause，但需要在最初打开关系时
 * 知道正确的锁时，就会用到这个。
 *
 * 注意：我们不关注是 FOR UPDATE 还是 FOR SHARE，
 * 因为表级锁无论哪种方式都是一样的。
 */
bool isLockedRefname(ParseState *fc_pstate, const char *fc_refname)
{
	ListCell   *fc_l;

	/* 
	 * 如果我们在父级指定为锁定 FOR UPDATE/SHARE 的子查询中，则像
	 * 是这里有一个通用的 FOR UPDATE 一样处理。
	 */
	if (fc_pstate->p_locked_from_parent)
		return true;

	foreach(fc_l, fc_pstate->p_locking_clause)
	{
		LockingClause *fc_lc = (LockingClause *) lfirst(fc_l);

		if (fc_lc->lockedRels == NIL)
		{
			/* 所有在查询中使用的表 */
			return true;
		}
		else
		{
			/* 仅仅是指定的表 */
			ListCell   *fc_l2;

			foreach(fc_l2, fc_lc->lockedRels)
			{
				RangeVar   *fc_thisrel = (RangeVar *) lfirst(fc_l2);

				if (strcmp(fc_refname, fc_thisrel->relname) == 0)
					return true;
			}
		}
	}
	return false;
}

/* 
 * 将给定的 nsitem/RTE 作为 pstate 的连接列表
 * 和/或命名空间列表中的顶级条目添加。（我们假设调用者已检查任何命名空间冲突。）
 * nsitem 始终标记为无条件可见，即不是仅 LATERAL。
 */
void addNSItemToQuery(ParseState *fc_pstate, ParseNamespaceItem *fc_nsitem,
				 bool fc_addToJoinList,
				 bool fc_addToRelNameSpace, bool fc_addToVarNameSpace)
{
	if (fc_addToJoinList)
	{
		RangeTblRef *fc_rtr = makeNode(RangeTblRef);

		fc_rtr->rtindex = fc_nsitem->p_rtindex;
		fc_pstate->p_joinlist = lappend(fc_pstate->p_joinlist, fc_rtr);
	}
	if (fc_addToRelNameSpace || fc_addToVarNameSpace)
	{
		/* 正确设置新 nsitem 的可见性标志 */
		fc_nsitem->p_rel_visible = fc_addToRelNameSpace;
		fc_nsitem->p_cols_visible = fc_addToVarNameSpace;
		fc_nsitem->p_lateral_only = false;
		fc_nsitem->p_lateral_ok = true;
		fc_pstate->p_namespace = lappend(fc_pstate->p_namespace, fc_nsitem);
	}
}

/* 
 * expandRTE -- 扩展 rangetable 条目的列
 *
 * 这会创建 RTE 的列名（如果提供了别名，则为别名，否则为
 * 实际名称）和每列的 Vars 列表。只有用户列被考虑。
 * 如果 include_dropped 为 false，则省略已删除的列。
 * 如果 include_dropped 为 true，则返回空字符串和 NULL 常量
 * （而不是 Vars！）作为已删除列。
 *
 * rtindex、sublevels_up 和 location 是在创建的 Vars 中
 * 使用的 varno、varlevelsup 和 location 值。通常情况下，rtindex
 * 应该与 RTE 在其 rangetable 中的实际位置匹配。
 *
 * 输出列表放入 *colnames 和 *colvars。
 * 如果只需要两种输出列表中的一种，请为不需要的输出指针传递 NULL。
 */
void expandRTE(RangeTblEntry *fc_rte, int fc_rtindex, int fc_sublevels_up,
		  int fc_location, bool fc_include_dropped,
		  List **fc_colnames, List **fc_colvars)
{
	int			fc_varattno;

	if (fc_colnames)
		*fc_colnames = NIL;
	if (fc_colvars)
		*fc_colvars = NIL;

	switch (fc_rte->rtekind)
	{
		case RTE_RELATION:
			/* 普通关系 RTE */
			fc_expandRelation(fc_rte->relid, fc_rte->eref,
						   fc_rtindex, fc_sublevels_up, fc_location,
						   fc_include_dropped, fc_colnames, fc_colvars);
			break;
		case RTE_SUBQUERY:
			{
				/* 子查询 RTE */
				ListCell   *fc_aliasp_item = list_head(fc_rte->eref->colnames);
				ListCell   *fc_tlistitem;

				fc_varattno = 0;
				foreach(fc_tlistitem, fc_rte->subquery->targetList)
				{
					TargetEntry *fc_te = (TargetEntry *) lfirst(fc_tlistitem);

					if (fc_te->resjunk)
						continue;
					fc_varattno++;
					Assert(fc_varattno == fc_te->resno);

					/* 
					 * 在刚解析的子查询 RTE 中，rte->eref->colnames 
					 * 应该始终有与子查询的非垃圾输出列完全相同的条目数。
					 * 但是，如果子查询 RTE 是通过扩展视图创建的，
					 * 也许子查询 tlist 现在可以有比外部查询解析时
					 * 更多的条目。这样的情况现在应该被防止，
					 * 因为 ApplyRetrieveRule 会扩展 colnames 列表以匹配。
					 * 但是出于谨慎，我们将保持后续代码
					 * 类似于这种方式：只是忽略任何缺少 colnames
					 * 条目的列。
					 */
					if (!fc_aliasp_item)
						break;

					if (fc_colnames)
					{
						char	   *fc_label = strVal(lfirst(fc_aliasp_item));

						*fc_colnames = lappend(*fc_colnames, makeString(pstrdup(fc_label)));
					}

					if (fc_colvars)
					{
						Var		   *fc_varnode;

						fc_varnode = makeVar(fc_rtindex, fc_varattno,
										  exprType((Node *) fc_te->expr),
										  exprTypmod((Node *) fc_te->expr),
										  exprCollation((Node *) fc_te->expr),
										  fc_sublevels_up);
						fc_varnode->location = fc_location;

						*fc_colvars = lappend(*fc_colvars, fc_varnode);
					}

					fc_aliasp_item = lnext(fc_rte->eref->colnames, fc_aliasp_item);
				}
			}
			break;
		case RTE_FUNCTION:
			{
				/* 函数 RTE */
				int			fc_atts_done = 0;
				ListCell   *fc_lc;

				foreach(fc_lc, fc_rte->functions)
				{
					RangeTblFunction *fc_rtfunc = (RangeTblFunction *) lfirst(fc_lc);
					TypeFuncClass fc_functypclass;
					Oid			fc_funcrettype = InvalidOid;
					TupleDesc	fc_tupdesc = NULL;

					/* 如果它有 coldeflist，它返回 RECORD */
					if (fc_rtfunc->funccolnames != NIL)
						fc_functypclass = TYPEFUNC_RECORD;
					else
						fc_functypclass = get_expr_result_type(fc_rtfunc->funcexpr,
															&fc_funcrettype,
															&fc_tupdesc);

					if (fc_functypclass == TYPEFUNC_COMPOSITE ||
						fc_functypclass == TYPEFUNC_COMPOSITE_DOMAIN)
					{
						/* 复合数据类型，例如表的行类型 */
						Assert(fc_tupdesc);
						fc_expandTupleDesc(fc_tupdesc, fc_rte->eref,
										fc_rtfunc->funccolcount, fc_atts_done,
										fc_rtindex, fc_sublevels_up, fc_location,
										fc_include_dropped, fc_colnames, fc_colvars);
					}
					else if (fc_functypclass == TYPEFUNC_SCALAR)
					{
						/* 基本数据类型，即标量 */
						if (fc_colnames)
							*fc_colnames = lappend(*fc_colnames,
												list_nth(fc_rte->eref->colnames,
														 fc_atts_done));

						if (fc_colvars)
						{
							Var		   *fc_varnode;

							fc_varnode = makeVar(fc_rtindex, fc_atts_done + 1,
											  fc_funcrettype,
											  exprTypmod(fc_rtfunc->funcexpr),
											  exprCollation(fc_rtfunc->funcexpr),
											  fc_sublevels_up);
							fc_varnode->location = fc_location;

							*fc_colvars = lappend(*fc_colvars, fc_varnode);
						}
					}
					else if (fc_functypclass == TYPEFUNC_RECORD)
					{
						if (fc_colnames)
						{
							List	   *fc_namelist;

							/* 提取列列表的适当子集 */
							fc_namelist = list_copy_tail(fc_rte->eref->colnames,
													  fc_atts_done);
							fc_namelist = list_truncate(fc_namelist,
													 fc_rtfunc->funccolcount);
							*fc_colnames = list_concat(*fc_colnames, fc_namelist);
						}

						if (fc_colvars)
						{
							ListCell   *fc_l1;
							ListCell   *fc_l2;
							ListCell   *fc_l3;
							int			fc_attnum = fc_atts_done;

							forthree(fc_l1, fc_rtfunc->funccoltypes,
									 fc_l2, fc_rtfunc->funccoltypmods,
									 fc_l3, fc_rtfunc->funccolcollations)
							{
								Oid			fc_attrtype = lfirst_oid(fc_l1);
								int32		fc_attrtypmod = lfirst_int(fc_l2);
								Oid			fc_attrcollation = lfirst_oid(fc_l3);
								Var		   *fc_varnode;

								fc_attnum++;
								fc_varnode = makeVar(fc_rtindex,
												  fc_attnum,
												  fc_attrtype,
												  fc_attrtypmod,
												  fc_attrcollation,
												  fc_sublevels_up);
								fc_varnode->location = fc_location;
								*fc_colvars = lappend(*fc_colvars, fc_varnode);
							}
						}
					}
					else
					{
						/* addRangeTableEntryForFunction 应该已经捕获了这一点 */
						elog(ERROR, "function in FROM has unsupported return type");
					}
					fc_atts_done += fc_rtfunc->funccolcount;
				}

				/* 如果有，追加序列列 */
				if (fc_rte->funcordinality)
				{
					if (fc_colnames)
						*fc_colnames = lappend(*fc_colnames,
											llast(fc_rte->eref->colnames));

					if (fc_colvars)
					{
						Var		   *fc_varnode = makeVar(fc_rtindex,
													  fc_atts_done + 1,
													  INT8OID,
													  -1,
													  InvalidOid,
													  fc_sublevels_up);

						*fc_colvars = lappend(*fc_colvars, fc_varnode);
					}
				}
			}
			break;
		case RTE_JOIN:
			{
				/* Join RTE */
				ListCell   *fc_colname;
				ListCell   *fc_aliasvar;

				Assert(list_length(fc_rte->eref->colnames) == list_length(fc_rte->joinaliasvars));

				fc_varattno = 0;
				forboth(fc_colname, fc_rte->eref->colnames, fc_aliasvar, fc_rte->joinaliasvars)
				{
					Node	   *fc_avar = (Node *) lfirst(fc_aliasvar);

					fc_varattno++;

					/* 
					 * 在普通解析过程中，连接中永远不会有任何已删除的列。
					 * 虽然该函数也被重写器和规划器使用，但它们
					 * 目前对任何 JOIN RTE 都没有调用它。因此，这
					 * 接下来的代码是死代码，但为了安全，还是像这样处理
					 * 这个情况可能比较好。
					 */
					if (fc_avar == NULL)
					{
						if (fc_include_dropped)
						{
							if (fc_colnames)
								*fc_colnames = lappend(*fc_colnames,
													makeString(pstrdup("")));
							if (fc_colvars)
							{
								/* 
								 * 这里无法使用连接的列类型（它可能会
								 * 被删除！）；但是，常量声称是什么类型
								 * 实际上并不重要。
								 */
								*fc_colvars = lappend(*fc_colvars,
												   makeNullConst(INT4OID, -1,
																 InvalidOid));
							}
						}
						continue;
					}

					if (fc_colnames)
					{
						char	   *fc_label = strVal(lfirst(fc_colname));

						*fc_colnames = lappend(*fc_colnames,
											makeString(pstrdup(fc_label)));
					}

					if (fc_colvars)
					{
						Var		   *fc_varnode;

						/* 
						 * 如果 joinaliasvars 条目是一个简单的 Var，则只需
						 * 复制它（调整 varlevelsup 和 location）；否则，它是一个
						 * JOIN USING 列，我们必须生成一个连接别名 Var。
						 * 这与 expandNSItemVars 扩展“join.*”时
						 * 产生的结果匹配，如果我们有访问连接的
						 * ParseNamespaceItem 的权限。
						 */
						if (IsA(fc_avar, Var))
						{
							fc_varnode = copyObject((Var *) fc_avar);
							fc_varnode->varlevelsup = fc_sublevels_up;
						}
						else
							fc_varnode = makeVar(fc_rtindex, fc_varattno,
											  exprType(fc_avar),
											  exprTypmod(fc_avar),
											  exprCollation(fc_avar),
											  fc_sublevels_up);
						fc_varnode->location = fc_location;

						*fc_colvars = lappend(*fc_colvars, fc_varnode);
					}
				}
			}
			break;
		case RTE_TABLEFUNC:
		case RTE_VALUES:
		case RTE_CTE:
		case RTE_NAMEDTUPLESTORE:
			{
				/* Tablefunc、Values、CTE 或 ENR RTE */
				ListCell   *fc_aliasp_item = list_head(fc_rte->eref->colnames);
				ListCell   *fc_lct;
				ListCell   *fc_lcm;
				ListCell   *fc_lcc;

				fc_varattno = 0;
				forthree(fc_lct, fc_rte->coltypes,
						 fc_lcm, fc_rte->coltypmods,
						 fc_lcc, fc_rte->colcollations)
				{
					Oid			fc_coltype = lfirst_oid(fc_lct);
					int32		fc_coltypmod = lfirst_int(fc_lcm);
					Oid			fc_colcoll = lfirst_oid(fc_lcc);

					fc_varattno++;

					if (fc_colnames)
					{
						/* 假设每个输出列有一个别名 */
						if (OidIsValid(fc_coltype))
						{
							char	   *fc_label = strVal(lfirst(fc_aliasp_item));

							*fc_colnames = lappend(*fc_colnames,
												makeString(pstrdup(fc_label)));
						}
						else if (fc_include_dropped)
							*fc_colnames = lappend(*fc_colnames,
												makeString(pstrdup("")));

						fc_aliasp_item = lnext(fc_rte->eref->colnames, fc_aliasp_item);
					}

					if (fc_colvars)
					{
						if (OidIsValid(fc_coltype))
						{
							Var		   *fc_varnode;

							fc_varnode = makeVar(fc_rtindex, fc_varattno,
											  fc_coltype, fc_coltypmod, fc_colcoll,
											  fc_sublevels_up);
							fc_varnode->location = fc_location;

							*fc_colvars = lappend(*fc_colvars, fc_varnode);
						}
						else if (fc_include_dropped)
						{
							/* 
							 * 常量声称是什么类型实际上并不重要。
							 */
							*fc_colvars = lappend(*fc_colvars,
											   makeNullConst(INT4OID, -1,
															 InvalidOid));
						}
					}
				}
			}
			break;
		case RTE_RESULT:
			/* 这些不暴露任何列，所以没有什么可做的 */
			break;
		default:
			elog(ERROR, "unrecognized RTE kind: %d", (int) fc_rte->rtekind);
	}
}

/*
 * expandRelation -- expandRTE 子程序
 */
static void fc_expandRelation(Oid fc_relid, Alias *fc_eref, int fc_rtindex, int fc_sublevels_up,
			   int fc_location, bool fc_include_dropped,
			   List **fc_colnames, List **fc_colvars)
{
	Relation	fc_rel;

	/* 获取元组描述并交给 expandTupleDesc 处理 */
	fc_rel = relation_open(fc_relid, AccessShareLock);
	fc_expandTupleDesc(fc_rel->rd_att, fc_eref, fc_rel->rd_att->natts, 0,
					fc_rtindex, fc_sublevels_up,
					fc_location, fc_include_dropped,
					fc_colnames, fc_colvars);
	relation_close(fc_rel, AccessShareLock);
}

/*
 * expandTupleDesc -- expandRTE 子程序
 *
 * 为 tupdesc 的前 "count" 个属性生成名称和/或变量，
 * 并将它们附加到 colnames/colvars。 "offset" 会添加到每个变量本应具有的 varattno
 * 上，并且我们还跳过了 eref->colnames 中的前 "offset"
 * 项目。 （这些条款允许在 RTE_FUNCTION RTE 中为单个复合返回函数使用此代码。）
 */
static void fc_expandTupleDesc(TupleDesc fc_tupdesc, Alias *fc_eref, int fc_count, int fc_offset,
				int fc_rtindex, int fc_sublevels_up,
				int fc_location, bool fc_include_dropped,
				List **fc_colnames, List **fc_colvars)
{
	ListCell   *fc_aliascell;
	int			fc_varattno;

	fc_aliascell = (fc_offset < list_length(fc_eref->colnames)) ?
		list_nth_cell(fc_eref->colnames, fc_offset) : NULL;

	Assert(fc_count <= fc_tupdesc->natts);
	for (fc_varattno = 0; fc_varattno < fc_count; fc_varattno++)
	{
		Form_pg_attribute fc_attr = TupleDescAttr(fc_tupdesc, fc_varattno);

		if (fc_attr->attisdropped)
		{
			if (fc_include_dropped)
			{
				if (fc_colnames)
					*fc_colnames = lappend(*fc_colnames, makeString(pstrdup("")));
				if (fc_colvars)
				{
					/*
					 * 这里不能使用 atttypid，但属性类型声明并不重要。
					 */
					*fc_colvars = lappend(*fc_colvars,
									   makeNullConst(INT4OID, -1, InvalidOid));
				}
			}
			if (fc_aliascell)
				fc_aliascell = lnext(fc_eref->colnames, fc_aliascell);
			continue;
		}

		if (fc_colnames)
		{
			char	   *fc_label;

			if (fc_aliascell)
			{
				fc_label = strVal(lfirst(fc_aliascell));
				fc_aliascell = lnext(fc_eref->colnames, fc_aliascell);
			}
			else
			{
				/* 如果别名用完，使用基础名称 */
				fc_label = NameStr(fc_attr->attname);
			}
			*fc_colnames = lappend(*fc_colnames, makeString(pstrdup(fc_label)));
		}

		if (fc_colvars)
		{
			Var		   *fc_varnode;

			fc_varnode = makeVar(fc_rtindex, fc_varattno + fc_offset + 1,
							  fc_attr->atttypid, fc_attr->atttypmod,
							  fc_attr->attcollation,
							  fc_sublevels_up);
			fc_varnode->location = fc_location;

			*fc_colvars = lappend(*fc_colvars, fc_varnode);
		}
	}
}

/*
 * expandNSItemVars
 *	  生成一个 Vars 列表，以及可选的列名列表，
 *	  用于 nsitem 的非丢弃列。
 *
 * 发出的 Vars 被标记为给定的 sublevels_up 和位置。
 *
 * 如果 colnames 不是 NULL，将在这里存储列的字符串项列表；
 * 注意，它仅是 RTE 的 eref 列表的一个子集，因此
 * 列表元素不能被修改。
 */
List * expandNSItemVars(ParseNamespaceItem *fc_nsitem,
				 int fc_sublevels_up, int fc_location,
				 List **fc_colnames)
{
	List	   *fc_result = NIL;
	int			fc_colindex;
	ListCell   *fc_lc;

	if (fc_colnames)
		*fc_colnames = NIL;
	fc_colindex = 0;
	foreach(fc_lc, fc_nsitem->p_names->colnames)
	{
		String	   *fc_colnameval = lfirst(fc_lc);
		const char *fc_colname = strVal(fc_colnameval);
		ParseNamespaceColumn *fc_nscol = fc_nsitem->p_nscolumns + fc_colindex;

		if (fc_nscol->p_dontexpand)
		{
			/* 跳过 */
		}
		else if (fc_colname[0])
		{
			Var		   *fc_var;

			Assert(fc_nscol->p_varno > 0);
			fc_var = makeVar(fc_nscol->p_varno,
						  fc_nscol->p_varattno,
						  fc_nscol->p_vartype,
						  fc_nscol->p_vartypmod,
						  fc_nscol->p_varcollid,
						  fc_sublevels_up);
			/* makeVar 不提供这些参数，因此手动设置： */
			fc_var->varnosyn = fc_nscol->p_varnosyn;
			fc_var->varattnosyn = fc_nscol->p_varattnosyn;
			fc_var->location = fc_location;
			fc_result = lappend(fc_result, fc_var);
			if (fc_colnames)
				*fc_colnames = lappend(*fc_colnames, fc_colnameval);
		}
		else
		{
			/* 丢弃列，忽略 */
			Assert(fc_nscol->p_varno == 0);
		}
		fc_colindex++;
	}
	return fc_result;
}

/*
 * expandNSItemAttrs -
 *	  "*" 扩展的工作马：为 nsitem 的属性生成目标条目列表
 *
 * pstate->p_next_resno 确定分配给 TLE 的 resnos。
 * 被引用的列在调用者请求时会被标记为需要 SELECT 访问。
 */
List * expandNSItemAttrs(ParseState *fc_pstate, ParseNamespaceItem *fc_nsitem,
				  int fc_sublevels_up, bool fc_require_col_privs, int fc_location)
{
	RangeTblEntry *fc_rte = fc_nsitem->p_rte;
	List	   *fc_names,
			   *fc_vars;
	ListCell   *fc_name,
			   *fc_var;
	List	   *fc_te_list = NIL;

	fc_vars = expandNSItemVars(fc_nsitem, fc_sublevels_up, fc_location, &fc_names);

	/*
	 * 需要对表的读取访问权限。这通常与下面的
	 * markVarForSelectPriv 调用冗余，但如果表具有零列则不是。
	 * 如果 nsitem 是用于连接的，我们不需要做任何事情：
	 * 其组件表在添加到范围表时会被标记为 ACL_SELECT。
	 * （此步骤仅对 UPDATE/DELETE 的目标关系更改事物，
	 * 该关系不能位于连接下。）
	 */
	if (fc_rte->rtekind == RTE_RELATION)
		fc_rte->requiredPerms |= ACL_SELECT;

	forboth(fc_name, fc_names, fc_var, fc_vars)
	{
		char	   *fc_label = strVal(lfirst(fc_name));
		Var		   *fc_varnode = (Var *) lfirst(fc_var);
		TargetEntry *fc_te;

		fc_te = makeTargetEntry((Expr *) fc_varnode,
							 (AttrNumber) fc_pstate->p_next_resno++,
							 fc_label,
							 false);
		fc_te_list = lappend(fc_te_list, fc_te);

		if (fc_require_col_privs)
		{
			/* 需要每一列的读取访问权限 */
			markVarForSelectPriv(fc_pstate, fc_varnode);
		}
	}

	Assert(fc_name == NULL && fc_var == NULL);	/* 列表长度不相同？ */

	return fc_te_list;
}

/*
 * get_rte_attribute_name
 *		从 RangeTblEntry 获取属性名称
 *
 * 这与 get_attname() 不同，因为我们会使用可用的别名。
 * 特别是，它将在子选择或连接的 RTE 上工作，而
 * get_attname() 仅适用于真实关系。
 *
 * 如果给定的 attnum 是 InvalidAttrNumber，将返回 "*" —— 该情况
 * 发生在 Var 代表关系的整个元组时。
 *
 * 不要在丢弃的属性上调用此函数，这是调用者的责任。
 * （对于这样的情况，你会得到某种答案，但可能没有意义。）
 */
char * get_rte_attribute_name(RangeTblEntry *fc_rte, AttrNumber fc_attnum)
{
	if (fc_attnum == InvalidAttrNumber)
		return "*";

	/*
	 * 如果有用户写的列别名，请使用它。
	 */
	if (fc_rte->alias &&
		fc_attnum > 0 && fc_attnum <= list_length(fc_rte->alias->colnames))
		return strVal(list_nth(fc_rte->alias->colnames, fc_attnum - 1));

	/*
	 * 如果 RTE 是一个关系，请访问系统目录而不是
	 * eref->colnames 列表。这稍慢一些，但如果列从 eref 列表构建以来已被重命名，
	 * 它将给出正确答案（这在规则中容易发生）。
	 */
	if (fc_rte->rtekind == RTE_RELATION)
		return get_attname(fc_rte->relid, fc_attnum, false);

	/*
	 * 否则使用 eref 中的列名。这里应该始终有一个。
	 */
	if (fc_attnum > 0 && fc_attnum <= list_length(fc_rte->eref->colnames))
		return strVal(list_nth(fc_rte->eref->colnames, fc_attnum - 1));

	/* 否则调用者给了我们一个无效的 attnum */
	elog(ERROR, "invalid attnum %d for rangetable entry %s",
		 fc_attnum, fc_rte->eref->aliasname);
	return NULL;				/* 保持编译器安静 */
}

/*
 * get_rte_attribute_is_dropped
 *		检查尝试的属性引用是否指向已丢弃的列
 */
bool get_rte_attribute_is_dropped(RangeTblEntry *fc_rte, AttrNumber fc_attnum)
{
	bool		fc_result;

	switch (fc_rte->rtekind)
	{
		case RTE_RELATION:
			{
				/*
				 * 纯关系 RTE --- 获取属性的目录条目
				 */
				HeapTuple	fc_tp;
				Form_pg_attribute fc_att_tup;

				fc_tp = SearchSysCache2(ATTNUM,
									 ObjectIdGetDatum(fc_rte->relid),
									 Int16GetDatum(fc_attnum));
				if (!HeapTupleIsValid(fc_tp))	/* 不应该发生 */
					elog(ERROR, "cache lookup failed for attribute %d of relation %u",
						 fc_attnum, fc_rte->relid);
				fc_att_tup = (Form_pg_attribute) GETSTRUCT(fc_tp);
				fc_result = fc_att_tup->attisdropped;
				ReleaseSysCache(fc_tp);
			}
			break;
		case RTE_SUBQUERY:
		case RTE_TABLEFUNC:
		case RTE_VALUES:
		case RTE_CTE:

			/*
			 * 子选择、表函数、值、CTE RTE 从未丢弃
			 * 列
			 */
			fc_result = false;
			break;
		case RTE_NAMEDTUPLESTORE:
			{
				/* 通过测试有效的 coltype 检查是否丢弃 */
				if (fc_attnum <= 0 ||
					fc_attnum > list_length(fc_rte->coltypes))
					elog(ERROR, "invalid varattno %d", fc_attnum);
				fc_result = !OidIsValid((list_nth_oid(fc_rte->coltypes, fc_attnum - 1)));
			}
			break;
		case RTE_JOIN:
			{
				/*
				 * 构造时连接 RTE 不会有丢弃列，
				 * 但存储规则中的连接可能包含已从
				 * 基础表丢弃的列，如果这些列在规则中没有明确引用。
				 * 这将通过 joinaliasvars 列表中的空指针通知我们。
				 */
				Var		   *fc_aliasvar;

				if (fc_attnum <= 0 ||
					fc_attnum > list_length(fc_rte->joinaliasvars))
					elog(ERROR, "invalid varattno %d", fc_attnum);
				fc_aliasvar = (Var *) list_nth(fc_rte->joinaliasvars, fc_attnum - 1);

				fc_result = (fc_aliasvar == NULL);
			}
			break;
		case RTE_FUNCTION:
			{
				/* 函数 RTE */
				ListCell   *fc_lc;
				int			fc_atts_done = 0;

				/*
				 * 只有返回命名复合类型的函数才可能有丢弃属性。
				 * 在这种情况下，我们必须查找结果类型以查看目前是否已丢弃此
				 * 列。因此首先，遍历函数，直到找到覆盖请求列的函数为止。
				 */
				foreach(fc_lc, fc_rte->functions)
				{
					RangeTblFunction *fc_rtfunc = (RangeTblFunction *) lfirst(fc_lc);

					if (fc_attnum > fc_atts_done &&
						fc_attnum <= fc_atts_done + fc_rtfunc->funccolcount)
					{
						TupleDesc	fc_tupdesc;

						/* 如果它有 coldeflist，它返回 RECORD */
						if (fc_rtfunc->funccolnames != NIL)
							return false;	/* 不能有任何丢弃列 */

						fc_tupdesc = get_expr_result_tupdesc(fc_rtfunc->funcexpr,
														  true);
						if (fc_tupdesc)
						{
							/* 复合数据类型，例如表的行类型 */
							Form_pg_attribute fc_att_tup;

							Assert(fc_tupdesc);
							Assert(fc_attnum - fc_atts_done <= fc_tupdesc->natts);
							fc_att_tup = TupleDescAttr(fc_tupdesc,
													fc_attnum - fc_atts_done - 1);
							return fc_att_tup->attisdropped;
						}
						/* 否则，它不能有任何丢弃列 */
						return false;
					}
					fc_atts_done += fc_rtfunc->funccolcount;
				}

				/* 如果我们到这里，必须是在寻找序号列 */
				if (fc_rte->funcordinality && fc_attnum == fc_atts_done + 1)
					return false;

				/* 这可能不会发生... */
				ereport(ERROR,
						(errcode(ERRCODE_UNDEFINED_COLUMN),
						 errmsg("column %d of relation \"%s\" does not exist",
								fc_attnum,
								fc_rte->eref->aliasname)));
				fc_result = false; /* 保持编译器安静 */
			}
			break;
		case RTE_RESULT:
			/* 这可能不会发生... */
			ereport(ERROR,
					(errcode(ERRCODE_UNDEFINED_COLUMN),
					 errmsg("column %d of relation \"%s\" does not exist",
							fc_attnum,
							fc_rte->eref->aliasname)));
			fc_result = false;		/* 保持编译器安静 */
			break;
		default:
			elog(ERROR, "unrecognized RTE kind: %d", (int) fc_rte->rtekind);
			fc_result = false;		/* 保持编译器安静 */
	}

	return fc_result;
}

/*
 * 给定目标列表和 resno，返回匹配的 TargetEntry
 *
 * 如果列表中不存在 resno，返回 NULL。
 *
 * 注意：我们需要搜索，而不仅仅是使用 list_nth() 索引，
 * 因为并非所有 tlists 都按 resno 排序。
 */
TargetEntry * get_tle_by_resno(List *fc_tlist, AttrNumber fc_resno)
{
	ListCell   *fc_l;

	foreach(fc_l, fc_tlist)
	{
		TargetEntry *fc_tle = (TargetEntry *) lfirst(fc_l);

		if (fc_tle->resno == fc_resno)
			return fc_tle;
	}
	return NULL;
}

/*
 * 给定查询和范围表索引，如果存在，返回关系的 RowMarkClause
 *
 * 如果关系未被选中用于更新/共享，则返回 NULL
 */
RowMarkClause *
get_parse_rowmark(Query *fc_qry, Index fc_rtindex)
{
	ListCell   *fc_l;

	foreach(fc_l, fc_qry->rowMarks)
	{
		RowMarkClause *fc_rc = (RowMarkClause *) lfirst(fc_l);

		if (fc_rc->rti == fc_rtindex)
			return fc_rc;
	}
	return NULL;
}

/*
 *	给定关系和属性名称，返回变量的 attnum
 *
 *	如果属性不存在（或被删除），则返回 InvalidAttrNumber。
 *
 *	这只应在关系已被
 *	table_open() 打开时使用。使用缓存版本 get_attnum()
 *	访问未打开的关系。
 */
int attnameAttNum(Relation fc_rd, const char *fc_attname, bool fc_sysColOK)
{
	int			fc_i;

	for (fc_i = 0; fc_i < RelationGetNumberOfAttributes(fc_rd); fc_i++)
	{
		Form_pg_attribute fc_att = TupleDescAttr(fc_rd->rd_att, fc_i);

		if (namestrcmp(&(fc_att->attname), fc_attname) == 0 && !fc_att->attisdropped)
			return fc_i + 1;
	}

	if (fc_sysColOK)
	{
		if ((fc_i = fc_specialAttNum(fc_attname)) != InvalidAttrNumber)
			return fc_i;
	}

	/* 失败时 */
	return InvalidAttrNumber;
}

/* specialAttNum()
 *
 * 检查属性名称以查看它是否是“特殊”的，例如“xmin”。
 * - thomas 2000-02-07
 *
 * 注意：这仅用于发现名称是否可能是系统属性。
 * 调用者需要确保它确实是该关系的一个属性。
 */
static int fc_specialAttNum(const char *fc_attname)
{
	const FormData_pg_attribute *fc_sysatt;

	fc_sysatt = SystemAttributeByName(fc_attname);
	if (fc_sysatt != NULL)
		return fc_sysatt->attnum;
	return InvalidAttrNumber;
}


/*
 * 给定属性ID，返回该属性的名称
 *
 * 这只能在关系已经被 table_open() 打开后使用。对于未打开的关系，请使用缓存版本 get_atttype()。
 */
const NameData *
attnumAttName(Relation fc_rd, int fc_attid)
{
	if (fc_attid <= 0)
	{
		const FormData_pg_attribute *fc_sysatt;

		fc_sysatt = SystemAttributeDefinition(fc_attid);
		return &fc_sysatt->attname;
	}
	if (fc_attid > fc_rd->rd_att->natts)
		elog(ERROR, "invalid attribute number %d", fc_attid);
	return &TupleDescAttr(fc_rd->rd_att, fc_attid - 1)->attname;
}

/*
 * 给定属性ID，返回该属性的类型
 *
 * 这只能在关系已经被 table_open() 打开后使用。对于未打开的关系，请使用缓存版本 get_atttype()。
 */
Oid attnumTypeId(Relation fc_rd, int fc_attid)
{
	if (fc_attid <= 0)
	{
		const FormData_pg_attribute *fc_sysatt;

		fc_sysatt = SystemAttributeDefinition(fc_attid);
		return fc_sysatt->atttypid;
	}
	if (fc_attid > fc_rd->rd_att->natts)
		elog(ERROR, "invalid attribute number %d", fc_attid);
	return TupleDescAttr(fc_rd->rd_att, fc_attid - 1)->atttypid;
}

/*
 * 给定属性ID，返回该属性的排序规则
 *
 * 这只能在关系已经被 table_open() 打开后使用。
 */
Oid attnumCollationId(Relation fc_rd, int fc_attid)
{
	if (fc_attid <= 0)
	{
		/* 所有系统属性的类型都是不可排序的。 */
		return InvalidOid;
	}
	if (fc_attid > fc_rd->rd_att->natts)
		elog(ERROR, "invalid attribute number %d", fc_attid);
	return TupleDescAttr(fc_rd->rd_att, fc_attid - 1)->attcollation;
}

/*
 * 生成关于缺失 RTE 的合适错误信息。
 *
 * 由于这是非常常见的错误类型，我们会努力生成一个有帮助的信息。
 */
void errorMissingRTE(ParseState *fc_pstate, RangeVar *fc_relation)
{
	RangeTblEntry *fc_rte;
	const char *fc_badAlias = NULL;

	/*
	 * 检查查询的 rangetable 中是否有潜在匹配项。 （注意：涉及 RangeVar 中的坏模式名称的情况会立即抛出错误。 这看起来可以。）
	 */
	fc_rte = fc_searchRangeTableForRel(fc_pstate, fc_relation);

	/*
	 * 如果我们找到一个有别名并且别名在命名空间中可见的匹配项，那么问题可能是使用了关系的真实名称而不是它的别名，即“SELECT foo.* FROM foo f”。 这个错误相当普遍，值得给出一个具体提示。
	 *
	 * 如果找到的匹配项不符合这些标准，可以假设问题是非法使用关系超出其作用域，如 MySQL-ism “SELECT ... FROM a, b LEFT JOIN c ON (a.x = c.y)”。
	 */
	if (fc_rte && fc_rte->alias &&
		strcmp(fc_rte->eref->aliasname, fc_relation->relname) != 0)
	{
		ParseNamespaceItem *fc_nsitem;
		int			fc_sublevels_up;

		fc_nsitem = refnameNamespaceItem(fc_pstate, NULL, fc_rte->eref->aliasname,
									  fc_relation->location,
									  &fc_sublevels_up);
		if (fc_nsitem && fc_nsitem->p_rte == fc_rte)
			fc_badAlias = fc_rte->eref->aliasname;
	}

	if (fc_rte)
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_TABLE),
				 errmsg("invalid reference to FROM-clause entry for table \"%s\"",
						fc_relation->relname),
				 (fc_badAlias ?
				  errhint("Perhaps you meant to reference the table alias \"%s\".",
						  fc_badAlias) :
				  errhint("There is an entry for table \"%s\", but it cannot be referenced from this part of the query.",
						  fc_rte->eref->aliasname)),
				 parser_errposition(fc_pstate, fc_relation->location)));
	else
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_TABLE),
				 errmsg("missing FROM-clause entry for table \"%s\"",
						fc_relation->relname),
				 parser_errposition(fc_pstate, fc_relation->location)));
}

/*
 * 生成关于缺失列的合适错误信息。
 *
 * 由于这是非常常见的错误类型，我们会努力生成一个有帮助的信息。
 */
void errorMissingColumn(ParseState *fc_pstate,
				   const char *fc_relname, const char *fc_colname, int fc_location)
{
	FuzzyAttrMatchState *fc_state;
	char	   *fc_closestfirst = NULL;

	/*
	 * 在整个 rtable 中搜索可能的匹配项。 如果我们找到一个，发出提示。
	 *
	 * TODO：改进此代码（和 errorMissingRTE），在适当时提到使用 LATERAL。
	 */
	fc_state = fc_searchRangeTableForCol(fc_pstate, fc_relname, fc_colname, fc_location);

	/*
	 * 提取最佳匹配的最近列字符串（如果有）。
	 *
	 * 尽管不可见，但根据未包含在返回状态中的属性编号推断出一个确切匹配 -- 这就是当 !closestfirst 时报告的内容。 也可能存在一个由于错误别名而被限定的确切匹配，在这种情况下 closestfirst 将被设置（因此提示与一般模糊情况相同）。
	 */
	if (fc_state->rfirst && AttributeNumberIsValid(fc_state->first))
		fc_closestfirst = strVal(list_nth(fc_state->rfirst->eref->colnames,
									   fc_state->first - 1));

	if (!fc_state->rsecond)
	{
		/*
		 * 处理建议零个或一个列的情况，包括所引用但不可见的确切匹配。
		 */
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_COLUMN),
				 fc_relname ?
				 errmsg("column %s.%s does not exist", fc_relname, fc_colname) :
				 errmsg("column \"%s\" does not exist", fc_colname),
				 fc_state->rfirst ? fc_closestfirst ?
				 errhint("Perhaps you meant to reference the column \"%s.%s\".",
						 fc_state->rfirst->eref->aliasname, fc_closestfirst) :
				 errhint("There is a column named \"%s\" in table \"%s\", but it cannot be referenced from this part of the query.",
						 fc_colname, fc_state->rfirst->eref->aliasname) : 0,
				 parser_errposition(fc_pstate, fc_location)));
	}
	else
	{
		/* 处理有两个同样有用的列提示的情况 */
		char	   *fc_closestsecond;

		fc_closestsecond = strVal(list_nth(fc_state->rsecond->eref->colnames,
										fc_state->second - 1));

		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_COLUMN),
				 fc_relname ?
				 errmsg("column %s.%s does not exist", fc_relname, fc_colname) :
				 errmsg("column \"%s\" does not exist", fc_colname),
				 errhint("Perhaps you meant to reference the column \"%s.%s\" or the column \"%s.%s\".",
						 fc_state->rfirst->eref->aliasname, fc_closestfirst,
						 fc_state->rsecond->eref->aliasname, fc_closestsecond),
				 parser_errposition(fc_pstate, fc_location)));
	}
}


/*
 * 检查一个完全解析的查询，并仅在查询底层的任何关系是临时关系（表、视图或物化视图）时返回真。
 */
bool isQueryUsingTempRelation(Query *fc_query)
{
	return fc_isQueryUsingTempRelation_walker((Node *) fc_query, NULL);
}

static bool fc_isQueryUsingTempRelation_walker(Node *fc_node, void *fc_context)
{
	if (fc_node == NULL)
		return false;

	if (IsA(fc_node, Query))
	{
		Query	   *fc_query = (Query *) fc_node;
		ListCell   *fc_rtable;

		foreach(fc_rtable, fc_query->rtable)
		{
			RangeTblEntry *fc_rte = lfirst(fc_rtable);

			if (fc_rte->rtekind == RTE_RELATION)
			{
				Relation	fc_rel = table_open(fc_rte->relid, AccessShareLock);
				char		fc_relpersistence = fc_rel->rd_rel->relpersistence;

				table_close(fc_rel, AccessShareLock);
				if (fc_relpersistence == RELPERSISTENCE_TEMP)
					return true;
			}
		}

		return query_tree_walker(fc_query,
								 fc_isQueryUsingTempRelation_walker,
								 fc_context,
								 QTW_IGNORE_JOINALIASES);
	}

	return expression_tree_walker(fc_node,
								  fc_isQueryUsingTempRelation_walker,
								  fc_context);
}
